[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