Log message for revision 30282: Added cyclic garbage collection support to Acquisition wrappers.
Changed: U Zope/trunk/lib/python/Acquisition/_Acquisition.c U Zope/trunk/lib/python/Acquisition/tests.py -=- Modified: Zope/trunk/lib/python/Acquisition/_Acquisition.c =================================================================== --- Zope/trunk/lib/python/Acquisition/_Acquisition.c 2005-05-06 15:08:45 UTC (rev 30281) +++ Zope/trunk/lib/python/Acquisition/_Acquisition.c 2005-05-06 15:08:46 UTC (rev 30282) @@ -87,7 +87,7 @@ static PyObject * CallMethodO(PyObject *self, PyObject *name, - PyObject *args, PyObject *kw) + PyObject *args, PyObject *kw) { if (! args && PyErr_Occurred()) return NULL; UNLESS(name=PyObject_GetAttr(self,name)) { @@ -115,25 +115,35 @@ (O)->ob_type==(PyTypeObject*)&XaqWrappertype) #define WRAPPER(O) ((Wrapper*)(O)) -static PyObject * -Wrapper__init__(Wrapper *self, PyObject *args) +static int +Wrapper__init__(Wrapper *self, PyObject *args, PyObject *kwargs) { PyObject *obj, *container; - UNLESS(PyArg_Parse(args,"(OO)",&obj,&container)) return NULL; + if (kwargs && PyDict_Size(kwargs) != 0) + { + PyErr_SetString(PyExc_TypeError, + "kwyword arguments not allowed"); + return -1; + } + UNLESS(PyArg_ParseTuple(args, "OO:__init__", &obj, &container)) return -1; + if (self == WRAPPER(obj)) { PyErr_SetString(PyExc_ValueError, "Cannot wrap acquisition wrapper in itself (Wrapper__init__)"); - return NULL; + return -1; } Py_INCREF(obj); - Py_INCREF(container); self->obj=obj; - self->container=container; - Py_INCREF(Py_None); - return Py_None; + + if (container != Py_None) + { + Py_INCREF(container); + self->container=container; + } + return 0; } /* ---------------------------------------------------------------- */ @@ -150,8 +160,7 @@ PyTuple_SET_ITEM(t,0,NULL); Py_DECREF(t); - if (r - && r->ob_refcnt==1 + if (r != NULL && isWrapper(r) && WRAPPER(r)->container && isWrapper(WRAPPER(r)->container) ) @@ -160,9 +169,21 @@ WRAPPER(WRAPPER(r)->container)->obj) ) { - /* Simplify wrapper */ - Py_XINCREF(WRAPPER(WRAPPER(r)->obj)->obj); - ASSIGN(WRAPPER(r)->obj, WRAPPER(WRAPPER(r)->obj)->obj); + if (r->ob_refcnt !=1 ) + { + t = PyObject_CallFunctionObjArgs((PyObject *)(r->ob_type), + WRAPPER(r)->obj, + WRAPPER(r)->container, + NULL); + Py_DECREF(r); + if (t==NULL) + return NULL; + r = t; + } + + /* Simplify wrapper */ + Py_XINCREF(WRAPPER(WRAPPER(r)->obj)->obj); + ASSIGN(WRAPPER(r)->obj, WRAPPER(WRAPPER(r)->obj)->obj); } return r; @@ -171,64 +192,66 @@ return NULL; } -static Wrapper *freeWrappers=0; -static int nWrappers=0; -#define MAX_CACHED_WRAPPERS 200 - static PyObject * -newWrapper(PyObject *obj, PyObject *container, PyTypeObject *Wrappertype) +Wrapper_descrget(Wrapper *self, PyObject *inst, PyObject *cls) { - Wrapper *self; - - if (freeWrappers) + + if (inst == NULL) { - self=freeWrappers; - freeWrappers=(Wrapper*)self->obj; - _Py_NewReference((PyObject *)self); - assert(self->ob_refcnt == 1); - self->ob_type=Wrappertype; - nWrappers--; + Py_INCREF(self); + return (PyObject *)self; } - else - { - UNLESS(self = PyObject_NEW(Wrapper, Wrappertype)) return NULL; - } + + return __of__((PyObject *)self, inst); +} - if (self == WRAPPER(obj)) { - PyErr_SetString(PyExc_ValueError, - "Cannot wrap acquisition wrapper in itself (newWrapper)"); - Py_DECREF(self); - return NULL; - } - Py_INCREF(Wrappertype); - Py_XINCREF(obj); - Py_XINCREF(container); - self->obj=obj; - self->container=container; - return OBJECT(self); -} +#define newWrapper(obj, container, Wrappertype) \ + PyObject_CallFunctionObjArgs(OBJECT(Wrappertype), obj, container, NULL) -static void -Wrapper_dealloc(Wrapper *self) +static int +Wrapper_traverse(Wrapper *self, visitproc visit, void *arg) { - Py_XDECREF(self->obj); - Py_XDECREF(self->container); - Py_DECREF(self->ob_type); + int vret; - if (nWrappers < MAX_CACHED_WRAPPERS) - { - self->obj=OBJECT(freeWrappers); - freeWrappers=self; - nWrappers++; + if (self->obj) { + vret = visit(self->obj, arg); + if (vret != 0) + return vret; } - else - { - PyObject_DEL(self); + if (self->container) { + vret = visit(self->container, arg); + if (vret != 0) + return vret; } + + return 0; } +static int +Wrapper_clear(Wrapper *self) +{ + PyObject *tmp; + + tmp = self->obj; + self->obj = NULL; + Py_XDECREF(tmp); + + tmp = self->container; + self->container = NULL; + Py_XDECREF(tmp); + + return 0; +} + +static void +Wrapper_dealloc(Wrapper *self) +{ + Wrapper_clear(self); + self->ob_type->tp_free((PyObject*)self); +} + static PyObject * Wrapper_special(Wrapper *self, char *name, PyObject *oname) { @@ -1113,10 +1136,7 @@ return NULL; } - static struct PyMethodDef Wrapper_methods[] = { - {"__init__", (PyCFunction)Wrapper__init__, 0, - "Initialize an Acquirer Wrapper"}, {"acquire", (PyCFunction)Wrapper_acquire_method, METH_VARARGS|METH_KEYWORDS, "Get an attribute, acquiring it if necessary"}, @@ -1155,12 +1175,27 @@ (reprfunc)Wrapper_str, /*tp_str*/ (getattrofunc)Wrapper_getattro, /*tp_getattr with object key*/ (setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/ - - /* Space for future expansion */ - 0L,0L, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC + , "Wrapper object for implicit acquisition", /* Documentation string */ - METHOD_CHAIN(Wrapper_methods), - (void*)(EXTENSIONCLASS_BINDABLE_FLAG), + /* tp_traverse */ (traverseproc)Wrapper_traverse, + /* tp_clear */ (inquiry)Wrapper_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Wrapper_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, + /* tp_descr_get */ (descrgetfunc)Wrapper_descrget, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc)Wrapper__init__, }; static PyExtensionClass XaqWrappertype = { @@ -1184,12 +1219,27 @@ (reprfunc)Wrapper_str, /*tp_str*/ (getattrofunc)Xaq_getattro, /*tp_getattr with object key*/ (setattrofunc)Wrapper_setattro, /*tp_setattr with object key*/ - - /* Space for future expansion */ - 0L,0L, - "Wrapper object for explicit acquisition", /* Documentation string */ - METHOD_CHAIN(Wrapper_methods), - (void*)(EXTENSIONCLASS_BINDABLE_FLAG), + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC + , + "Wrapper object for implicit acquisition", /* Documentation string */ + /* tp_traverse */ (traverseproc)Wrapper_traverse, + /* tp_clear */ (inquiry)Wrapper_clear, + /* tp_richcompare */ (richcmpfunc)0, + /* tp_weaklistoffset */ (long)0, + /* tp_iter */ (getiterfunc)0, + /* tp_iternext */ (iternextfunc)0, + /* tp_methods */ Wrapper_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, + /* tp_descr_get */ (descrgetfunc)Wrapper_descrget, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc)Wrapper__init__, }; static PyObject * @@ -1262,7 +1312,7 @@ if (! filter) return PyObject_GetAttr(self, name); /* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */ - UNLESS (self=newWrapper(self, NULL, (PyTypeObject*)&Wrappertype)) + UNLESS (self=newWrapper(self, Py_None, (PyTypeObject*)&Wrappertype)) return NULL; result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self), Modified: Zope/trunk/lib/python/Acquisition/tests.py =================================================================== --- Zope/trunk/lib/python/Acquisition/tests.py 2005-05-06 15:08:45 UTC (rev 30281) +++ Zope/trunk/lib/python/Acquisition/tests.py 2005-05-06 15:08:46 UTC (rev 30282) @@ -369,6 +369,7 @@ Traceback (most recent call last): ... AttributeError: id + >>> Acquisition.aq_acquire(c, 'id', ... lambda searched, parent, name, ob, extra: extra, ... 1) @@ -1386,7 +1387,7 @@ >>> w = ImplicitAcquisitionWrapper(a.b) Traceback (most recent call last): ... - TypeError: argument must be 2-item sequence, not B + TypeError: __init__() takes exactly 2 arguments (1 given) We can reassign aq_parent @@ -1399,8 +1400,12 @@ >>> w = ImplicitAcquisitionWrapper() Traceback (most recent call last): ... - TypeError: function takes at least one argument + TypeError: __init__() takes exactly 2 arguments (0 given) + >>> w = ImplicitAcquisitionWrapper(obj=1) + Traceback (most recent call last): + ... + TypeError: kwyword arguments not allowed """ def test_cant_pickle_acquisition_wrappers_classic(): @@ -1524,6 +1529,71 @@ rval = rval + indent + id + "\n" return rval + + +def test_Basic_gc(): + """Test to make sure that EC instances participate in GC + + >>> from ExtensionClass import Base + >>> import gc + >>> thresholds = gc.get_threshold() + >>> gc.set_threshold(0) + + >>> for B in I, E: + ... class C1(B): + ... pass + ... + ... class C2(Base): + ... def __del__(self): + ... print 'removed' + ... + ... a=C1('a') + ... a.b = C1('a.b') + ... a.b.a = a + ... a.b.c = C2() + ... ignore = gc.collect() + ... del a + ... removed = gc.collect() + ... print removed > 0 + removed + True + removed + True + + >>> gc.set_threshold(*thresholds) + + """ + +def test_Wrapper_gc(): + """Test to make sure that EC instances participate in GC + + >>> import gc + >>> thresholds = gc.get_threshold() + >>> gc.set_threshold(0) + + >>> for B in I, E: + ... class C: + ... def __del__(self): + ... print 'removed' + ... + ... a=B('a') + ... a.b = B('b') + ... a.a_b = a.b # circ ref through wrapper + ... a.b.c = C() + ... ignored = gc.collect() + ... del a + ... removed = gc.collect() + ... removed > 0 + removed + True + removed + True + + >>> gc.set_threshold(*thresholds) + +""" + + import unittest _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins