[Zodb-checkins] CVS: Zope3/lib/python/Persistence - cPersistenceAPI.h:1.7 cPersistence.h:1.7 cPersistence.c:1.22 __init__.py:1.10 Module.py:1.30 Class.py:1.6

Jeremy Hylton jeremy@zope.com
Thu, 10 Oct 2002 17:36:19 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv22620/lib/python/Persistence

Modified Files:
	cPersistenceAPI.h cPersistence.h cPersistence.c __init__.py 
	Module.py Class.py 
Log Message:
Eliminate BasePersistent! (Take two.)

All Zope3 tests pass with python CVS and python 2.2.2b1.

Python 2.2 has peculiar rules about whether a type's instances should
have an __dict__, which have been fixed in 2.3.  The new rules make
the BasePersistent unnecessary, because Persistent does not need to
explicitly create an __dict__.

There is a vast simplification to the code by eliminating
BasePersistent and the corresponding C type.  All C code uses a single
type (PyPersist_TYPE) and struct (PyPersistObject *).  Yay!

But we've still got to support Python 2.2 (bummer) and it's two big a
change to make some version of 2.2 behave like 2.3.  So we still need
some sort of hack to make this all work with 2.2.  The answer is a
persistent meta class that sets the dictoffset correctly and adds an
__dict__ descriptor.

The change has fairly broad changes to the Persistence package,
because the Python code already used PersistentMetaClass for
persistent clas support.  Rename that meta class to
PersistentClassMetaClass, which is descriptive but too verbose.  (Then
again all the names are too verbose.)


=== Zope3/lib/python/Persistence/cPersistenceAPI.h 1.6 => 1.7 ===
--- Zope3/lib/python/Persistence/cPersistenceAPI.h:1.6	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/cPersistenceAPI.h	Thu Oct 10 17:36:18 2002
@@ -21,13 +21,12 @@
 
 static PyPersist_C_API_struct *PyPersist_C_API;
 
-#define PyPersist_BASE_TYPE PyPersist_C_API->base_type
-#define PyPersist_INSTANCE_TYPE PyPersist_C_API->instance_type
+#define PyPersist_TYPE PyPersist_C_API->base_type
 
 #define PyPersist_INCREF(O) \
     if (((O)->po_state == UPTODATE) \
 	|| ((O)->po_state == GHOST \
-	    && PyPersist_C_API->load((PyPersistBaseObject *)(O)))) \
+	    && PyPersist_C_API->load((PyPersistObject *)(O)))) \
 	(O)->po_state = STICKY;
 
 #define PyPersist_DECREF(O) \
@@ -41,24 +40,24 @@
     ((O)->po_state == STICKY || (O)->po_state == CHANGED)
 
 #define PyPersist_CHANGED(O) \
-    PyPersist_C_API->reg_mgr((PyPersistBaseObject *)(O))
+    PyPersist_C_API->reg_mgr((PyPersistObject *)(O))
 
 #define PyPersist_SetATime(O) \
-    PyPersist_C_API->set_atime((PyPersistBaseObject *)(O))
+    PyPersist_C_API->set_atime((PyPersistObject *)(O))
 
 /* Macros for compatibility with ZODB 3 C extensions. */
 
 #define PER_USE_OR_RETURN(O, R) \
 { \
     if (((O)->po_state == GHOST) \
-	&& (PyPersist_C_API->load((PyPersistBaseObject *)(O)) < 0)) { \
+	&& (PyPersist_C_API->load((PyPersistObject *)(O)) < 0)) { \
         (O)->po_state = STICKY; \
 	return (R); \
     } else if ((O)->po_state == UPTODATE) \
 	(O)->po_state = STICKY; \
 }
 
-#define PER_CHANGED(O) PyPersist_C_API->reg_mgr((PyPersistBaseObject *)(O))
+#define PER_CHANGED(O) PyPersist_C_API->reg_mgr((PyPersistObject *)(O))
 
 #define PER_ALLOW_DEACTIVATION(O) \
 { \
@@ -73,9 +72,9 @@
 }
 
 #define PER_USE(O) \
-    ((((PyPersistBaseObject *)(O))->po_state != GHOST) \
-     || (PyPersist_C_API->load((PyPersistBaseObject *)(O)) >= 0) \
-     ? ((((PyPersistBaseObject *)(O))->po_state == UPTODATE) \
-	? (((PyPersistBaseObject *)(O))->po_state = STICKY) : 1) : 0)
+    ((((PyPersistObject *)(O))->po_state != GHOST) \
+     || (PyPersist_C_API->load((PyPersistObject *)(O)) >= 0) \
+     ? ((((PyPersistObject *)(O))->po_state == UPTODATE) \
+	? (((PyPersistObject *)(O))->po_state = STICKY) : 1) : 0)
 
-#define PER_ACCESSED(O) PyPersist_C_API->set_atime((PyPersistBaseObject *)O)
+#define PER_ACCESSED(O) PyPersist_C_API->set_atime((PyPersistObject *)O)


=== Zope3/lib/python/Persistence/cPersistence.h 1.6 => 1.7 ===
--- Zope3/lib/python/Persistence/cPersistence.h:1.6	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/cPersistence.h	Thu Oct 10 17:36:18 2002
@@ -40,21 +40,13 @@
     int po_atime; \
     enum PyPersist_State po_state;
 
-#define PyPersist_INSTANCE_HEAD \
-    PyPersist_HEAD \
-    PyObject *po_dict; 
-
 typedef struct {
     PyPersist_HEAD
-} PyPersistBaseObject;
-
-typedef struct {
-    PyPersist_INSTANCE_HEAD
 } PyPersistObject;
 
-extern PyObject *_PyPersist_Load(PyPersistBaseObject *);
-extern PyObject *_PyPersist_RegisterDataManager(PyPersistBaseObject *);
-extern void _PyPersist_SetATime(PyPersistBaseObject *);
+extern PyObject *_PyPersist_Load(PyPersistObject *);
+extern PyObject *_PyPersist_RegisterDataManager(PyPersistObject *);
+extern void _PyPersist_SetATime(PyPersistObject *);
 
 /* A struct to encapsulation the PyPersist C API for use by other
    dynamically load extensions.
@@ -62,9 +54,8 @@
 
 typedef struct {
     PyTypeObject *base_type;
-    PyTypeObject *instance_type;
-    PyObject *(*load)(PyPersistBaseObject *);
-    PyObject *(*reg_mgr)(PyPersistBaseObject *);
-    void (*set_atime)(PyPersistBaseObject *);
+    PyObject *(*load)(PyPersistObject *);
+    PyObject *(*reg_mgr)(PyPersistObject *);
+    void (*set_atime)(PyPersistObject *);
 } PyPersist_C_API_struct;
 


=== Zope3/lib/python/Persistence/cPersistence.c 1.21 => 1.22 === (413/513 lines abridged)
--- Zope3/lib/python/Persistence/cPersistence.c:1.21	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/cPersistence.c	Thu Oct 10 17:36:18 2002
@@ -20,35 +20,10 @@
 "\n"
 "$Id$\n";
 
-/* cPersistence defines two Python types and a C API for them.
+static PyTypeObject PyPersist_MetaType;
 
-   This table summarizes the stuff connected to each type.
-
-   PyTypeObject	   PyPersistBase_Type    	PyPersist_Type
-   supports        persistent objects in C 	persistent objects in Python
-   PyObject *      PyPersistBaseObject *        PyPersistObject *
-   C struct        PyPersist_HEAD		PyPersist_INSTANCE_HEAD
-   C API macro     PyPersist_BASE_TYPE          PyPersist_INSTANCE_TYPE       
-   Python object   Persistence.BasePersistent   Persistence.Persistent
-
-   The two types share many of the same tp_xxx implementations.  The
-   key difference between the base type and the instance type is that
-   the latter has room for an __dict__.  The Persistence.Persistent
-   object is used as a base class for peristent object classes written
-   in Python.
-
-   The functions that can operate on either type take a
-   PyPersistBaseObject * argument.  If the function needs to access
-   the po_dict slot, it must first check obj->ob_type->tp_dictoffset.
-   For a PyPersistBase_Type object, the dictoffset is 0.
-
-   The getstate and setstate implementations only work with
-   PyPersist_Type objects, because they depend on having an __dict__.
-
-   The base type exists to support C types that are also persistent.
-   The only examples of such types are in Persistence.BTrees.
-
-*/
+/* Python version of the simple_new function; */
+static PyObject *py_simple_new = NULL;
 
 /* A helper function that registers a persistent object with its data
    manager.
@@ -56,14 +31,8 @@
 
 static PyObject *s_register = NULL;
 
-/* Python version of the simple_new function; */
-static PyObject *py_simple_new = NULL;
-
-
-

[-=- -=- -=- 413 lines omitted -=- -=- -=-]

 static PyPersist_C_API_struct c_api = {
-    &PyPersistBase_Type,
     &PyPersist_Type,
     _PyPersist_Load,
     _PyPersist_RegisterDataManager,
@@ -891,16 +875,23 @@
 {
     PyObject *m, *d, *v;
 
-    PyPersistBase_Type.ob_type = &PyType_Type;
-    PyPersistBase_Type.tp_new = PyType_GenericNew;
-    PyPersist_Type.ob_type = &PyType_Type;
-    PyPersist_Type.tp_new = PyType_GenericNew;
-    if (PyType_Ready(&PyPersistBase_Type) < 0)
+    /* It's easier to initialize a few fields by assignment than to
+       fill out the entire type structure in an initializer. 
+    */
+    PyPersist_MetaType.ob_type = &PyType_Type;
+    PyPersist_MetaType.tp_alloc = PyPersist_Alloc;
+    PyPersist_MetaType.tp_new = PyPersist_New;
+    PyPersist_MetaType.tp_base = &PyType_Type;
+    PyPersist_MetaType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+	Py_TPFLAGS_BASETYPE;
+    PyPersist_MetaType.tp_traverse = PyType_Type.tp_traverse;
+    PyPersist_MetaType.tp_clear = PyType_Type.tp_clear;
+    if (PyType_Ready(&PyPersist_MetaType) < 0)
 	return;
+
+    PyPersist_Type.tp_new = PyType_GenericNew;
     if (PyType_Ready(&PyPersist_Type) < 0)
 	return;
-    if (persist_set_interface(&PyPersistBase_Type) < 0)
-	return;
     if (persist_set_interface(&PyPersist_Type) < 0)
 	return;
 
@@ -915,10 +906,9 @@
     Py_INCREF(&PyPersist_Type);
     if (PyDict_SetItemString(d, "Persistent", (PyObject *)&PyPersist_Type) < 0)
 	return;
-
-    Py_INCREF(&PyPersistBase_Type);
-    if (PyDict_SetItemString(d, "BasePersistent", 
-			     (PyObject *)&PyPersistBase_Type) < 0)
+    Py_INCREF(&PyPersist_MetaType);
+    if (PyDict_SetItemString(d, "PersistentMetaClass", 
+			     (PyObject *)&PyPersist_MetaType) < 0)
 	return;
 
     v = PyCObject_FromVoidPtr(&c_api, NULL);


=== Zope3/lib/python/Persistence/__init__.py 1.9 => 1.10 ===
--- Zope3/lib/python/Persistence/__init__.py:1.9	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/__init__.py	Thu Oct 10 17:36:18 2002
@@ -11,15 +11,15 @@
 # FOR A PARTICULAR PURPOSE.
 # 
 ##############################################################################
-"""Provide access to Persistent and BasePersistent C extension types."""
+"""Provide access to Persistent C extension types."""
 
-from cPersistence import Persistent, BasePersistent
+from cPersistence import Persistent, PersistentMetaClass
 
 # Wire up simple constructor
 import cPersistence
-simple_new = cPersistence.simple_new
-del cPersistence.simple_new
-del cPersistence
+##simple_new = cPersistence.simple_new
+##del cPersistence.simple_new
+##del cPersistence
 import copy_reg
-copy_reg.constructor(simple_new)
-del copy_reg
+copy_reg.constructor(cPersistence.simple_new)
+##del copy_reg


=== Zope3/lib/python/Persistence/Module.py 1.29 => 1.30 ===
--- Zope3/lib/python/Persistence/Module.py:1.29	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/Module.py	Thu Oct 10 17:36:18 2002
@@ -22,7 +22,7 @@
 
 from Persistence import Persistent
 from Persistence.cPersistence import GHOST
-from Persistence.Class import PersistentMetaClass
+from Persistence.Class import PersistentClassMetaClass
 from Persistence.Function import PersistentFunction
 from Persistence.IPersistentModuleManager import IPersistentModuleManager
 from Persistence.IPersistentModuleRegistry \
@@ -135,7 +135,7 @@
         for k, v in new.items():
             if isinstance(v, function):
                 v = new[k] = PersistentFunction(v, module)
-            elif isinstance(v.__class__, PersistentMetaClass):
+            elif isinstance(v.__class__, PersistentClassMetaClass):
                 v.__class__.fixup(module)
             # XXX need to check for classes that are not persistent!
 


=== Zope3/lib/python/Persistence/Class.py 1.5 => 1.6 ===
--- Zope3/lib/python/Persistence/Class.py:1.5	Thu Oct 10 04:20:06 2002
+++ Zope3/lib/python/Persistence/Class.py	Thu Oct 10 17:36:18 2002
@@ -13,7 +13,7 @@
 ##############################################################################
 """Persistent Classes."""
 
-from Persistence import Persistent
+from Persistence import Persistent, PersistentMetaClass
 from Persistence.cPersistence import UPTODATE, CHANGED, STICKY, GHOST
 from Persistence.IPersistent import IPersistent
 from Persistence.Function import PersistentFunction
@@ -22,6 +22,10 @@
 from types import FunctionType as function
 import time
 
+# XXX There is a lot of magic here to give classes and instances
+# separate sets of attributes.  This code should be documented, as it
+# it quite delicate, and it should be move to a separate module.
+
 class ExtClassDescr(object):
     """Maintains seperate class and instance descriptors for an attribute.
 
@@ -144,7 +148,7 @@
             return o
     return default
 
-class PersistentMetaClass(type):
+class PersistentClassMetaClass(PersistentMetaClass):
 
     # an attempt to make persistent classes look just like other
     # persistent objects by providing class attributes and methods
@@ -158,7 +162,7 @@
     _pc_init = 0
 
     def __new__(meta, name, bases, dict):
-        cls = super(PersistentMetaClass, meta).__new__(meta, name, bases, dict)
+        cls = super(PersistentClassMetaClass, meta).__new__(meta, name, bases, dict)
         # helper functions
         def extend_attr(attr, v):
             prev = findattr(cls, attr, None)
@@ -191,7 +195,7 @@
             if cls._p_state is None:
                 cls._p_activate()
                 cls._p_atime = int(time.time() % 86400)
-        return super(PersistentMetaClass, cls).__getattribute__(name)
+        return super(PersistentClassMetaClass, cls).__getattribute__(name)
 
     def __setattr__(cls, attr, val):
         if not attr.startswith("_pc_") and cls._pc_init:
@@ -201,7 +205,7 @@
                 if set is not None:
                     set(None, val)
                     return
-        super(PersistentMetaClass, cls).__setattr__(attr, val)
+        super(PersistentClassMetaClass, cls).__setattr__(attr, val)
 
     def __delattr__(cls, attr):
         if attr.startswith('_p_'):
@@ -210,7 +214,7 @@
                 pass
             else:
                 return
-        super(PersistentMetaClass, cls).__delattr__(attr)
+        super(PersistentClassMetaClass, cls).__delattr__(attr)
     
     def __getstate__(cls):
         dict = {}
@@ -242,14 +246,14 @@
 
 class PersistentBaseClass(Persistent):
 
-    __metaclass__ = PersistentMetaClass
+    __metaclass__ = PersistentClassMetaClass
 
 def _test():
     global PC, P
 
     class PC(Persistent):
 
-        __metaclass__ = PersistentMetaClass
+        __metaclass__ = PersistentClassMetaClass
 
         def __init__(self):
             self.x = 1