[Zope-Checkins] SVN: Zope/trunk/lib/python/Acquisition/ Added
cyclic garbage collection support to Acquisition wrappers.
Jim Fulton
jim at zope.com
Fri May 6 11:08:46 EDT 2005
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
More information about the Zope-Checkins
mailing list