[Zope-Checkins] CVS: Zope/lib/python/ExtensionClass - ExtensionClass.h:1.1.2.3 _ExtensionClass.c:1.1.2.3 __init__.py:1.1.2.3 tests.py:1.1.2.2

Jim Fulton cvs-admin at zope.org
Tue Oct 28 13:46:52 EST 2003


Update of /cvs-repository/Zope/lib/python/ExtensionClass
In directory cvs.zope.org:/tmp/cvs-serv17676/lib/python/ExtensionClass

Modified Files:
      Tag: zodb33-devel-branch
	ExtensionClass.h _ExtensionClass.c __init__.py tests.py 
Log Message:
Added support for the EXTENSIONCLASS_NOINSTDICT_FLAG flag.

Added pickling support.

Added __basicnew__ support, for old pickles.

Moved some basic tests to the __init__ file as documentation.


=== Zope/lib/python/ExtensionClass/ExtensionClass.h 1.1.2.2 => 1.1.2.3 ===
--- Zope/lib/python/ExtensionClass/ExtensionClass.h:1.1.2.2	Thu Oct 23 09:03:04 2003
+++ Zope/lib/python/ExtensionClass/ExtensionClass.h	Tue Oct 28 13:46:51 2003
@@ -88,11 +88,23 @@
 #define EC PyTypeObject
 #define PyExtensionClass PyTypeObject
 
+#define EXTENSIONCLASS_BINDABLE_FLAG      1 << 2
+#define EXTENSIONCLASS_NOINSTDICT_FLAG    1 << 5
+
 typedef struct {
   PyObject_HEAD
 } _emptyobject;
 
 static struct ExtensionClassCAPIstruct {
+
+/*****************************************************************************
+
+  WARNING: THIS STRUCT IS PRIVATE TO THE EXTENSION CLASS INTERFACE
+           IMPLEMENTATION AND IS SUBJECT TO CHANGE !!!
+
+ *****************************************************************************/
+
+
   PyObject *(*EC_findiattrs_)(PyObject *self, char *cname);
   int (*PyExtensionClass_Export_)(PyObject *dict, char *name, 
                                   PyTypeObject *typ);
@@ -122,7 +134,7 @@
    should use Py_FindAttr.
    */
 
-#define EC_findiattrs(O, N) PyExtensionClassCAPI->EC_findiattrs_((O),(N))
+#define EC_findiattrs (PyExtensionClassCAPI->EC_findiattrs_)
 
 #define Py_FindMethod(M,SELF,NAME) (EC_findiattrs((SELF),(NAME)))
 
@@ -146,7 +158,7 @@
    lookup methods or attributes that are not managed by the base type
    directly.  The macro is generally used to search for attributes
    after other attribute searches have failed.  */
-#define Py_FindAttr(SELF,NAME) (ECBaseType->tp_getattro(SELF, NAME))
+#define Py_FindAttr (ECBaseType->tp_getattro)
 
 /* Do attribute assignment for an attribute.
 
@@ -157,9 +169,6 @@
    */
 #define PyEC_SetAttr(SELF,NAME,V) (ECBaseType->tp_setattro(SELF, NAME, V))
 
-#define EXTENSIONCLASS_BINDABLE_FLAG      1 << 2
-
-
 
 /* Convert a method list to a method chain.  */
 #define METHOD_CHAIN(DEF) (traverseproc)(DEF)
@@ -236,5 +245,8 @@
 #define ExtensionClassImported \
   ((PyExtensionClassCAPI != NULL) || \
    (PyExtensionClassCAPI = PyCObject_Import("ExtensionClass","CAPI2")))
+
+#define PyMem_DEL(O) (O)->ob_type->tp_free((PyObject*)(O))
+#define PyObject_DEL(O) (O)->ob_type->tp_free((PyObject*)(O))
 
 #endif /* EXTENSIONCLASS_H */


=== Zope/lib/python/ExtensionClass/_ExtensionClass.c 1.1.2.2 => 1.1.2.3 ===
--- Zope/lib/python/ExtensionClass/_ExtensionClass.c:1.1.2.2	Thu Oct 23 09:03:04 2003
+++ Zope/lib/python/ExtensionClass/_ExtensionClass.c	Tue Oct 28 13:46:51 2003
@@ -22,6 +22,8 @@
 #define EC PyTypeObject
 
 static PyObject *str__of__, *str__get__, *str__class_init__, *str__init__;
+static PyObject *str__slotnames__, *copy_reg_slotnames, *__newobj__;
+static PyObject *str__getnewargs__, *str__getstate__, *str__new__;
 
 #define OBJECT(O) ((PyObject *)(O))
 #define TYPE(O) ((PyTypeObject *)(O))
@@ -144,7 +146,8 @@
                           if (res->ob_type->ob_type == &ExtensionClassType
                               && res->ob_type->tp_descr_get != NULL)
                             res = res->ob_type->tp_descr_get(
-                                                 res, obj, OBJECT(&BaseType));
+                                                 res, obj, 
+                                                 OBJECT(obj->ob_type));
                           else
                             Py_INCREF(res);
                           goto done;
@@ -163,38 +166,238 @@
 		goto done;
 	}
 
+        /* CHANGED: Just use the name. Don't format. */
         PyErr_SetObject(PyExc_AttributeError, name);
   done:
 	Py_DECREF(name);
 	return res;
 }
 
+/* It's a dang shame we can't inherit __get/setstate__ from object :( */
+
+static PyObject *
+Base_slotnames(PyTypeObject *cls)
+{
+  PyObject *slotnames;
+
+  slotnames = PyDict_GetItem(cls->tp_dict, str__slotnames__);
+  if (slotnames != NULL) 
+    {
+      Py_INCREF(slotnames);
+      return slotnames;
+    }
+
+  slotnames = PyObject_CallFunctionObjArgs(copy_reg_slotnames, OBJECT(cls), 
+                                           NULL);
+  if (slotnames != NULL &&
+      slotnames != Py_None &&
+      ! PyList_Check(slotnames))
+    {
+      PyErr_SetString(PyExc_TypeError,
+                      "copy_reg._slotnames didn't return a list or None");
+      Py_DECREF(slotnames);
+      slotnames = NULL;
+    }
+  
+  return slotnames;
+}
+
+static PyObject *
+getdict(PyObject *self)
+{
+  PyObject **dict;
+
+  dict = _PyObject_GetDictPtr(self);
+  if (dict)
+    return *dict;
+  return NULL;
+}
+
+
+static PyObject *
+Base___getstate__(PyObject *self)
+{
+  PyObject *slotnames=NULL, *slots=NULL, *state=NULL;
+  int n=0;
+
+  slotnames = Base_slotnames(self->ob_type);
+  if (slotnames == NULL)
+    return NULL;
+
+  state = getdict(self);
+  if (state == NULL) 
+    state = Py_None;
+  Py_INCREF(state);
+
+  if (slotnames != Py_None)
+    {
+      int i;
+
+      slots = PyDict_New();
+      if (slots == NULL)
+        goto end;
+
+      for (i = 0; i < PyList_GET_SIZE(slotnames); i++) 
+        {
+          PyObject *name, *value;
+          name = PyList_GET_ITEM(slotnames, i);
+          value = PyObject_GetAttr(self, name);
+          if (value == NULL)
+            PyErr_Clear();
+          else 
+            {
+              int err = PyDict_SetItem(slots, name, value);
+              Py_DECREF(value);
+              if (err)
+                goto end;
+              n++;
+            }
+        }
+    }
+
+  if (n) 
+    state = Py_BuildValue("(NO)", state, slots);
+
+ end:
+  Py_XDECREF(slotnames);
+  Py_XDECREF(slots);
+  
+  return state;
+}
+
+static int
+Base_setattrs_from_dict(PyObject *self, PyObject *dict)
+{
+  PyObject *key, *value;
+  int pos = 0;
+  
+  if (! PyDict_Check(dict))
+    {
+      PyErr_SetString(PyExc_TypeError, "Expected dictionary");
+      return -1;
+    }
+  
+  while (PyDict_Next(dict, &pos, &key, &value)) 
+    {
+      if (key != NULL && value != NULL &&
+          (PyObject_SetAttr(self, key, value) < 0)
+          )
+        return -1;
+    }
+  return 0;
+}
+
+static PyObject *
+Base___setstate__(PyObject *self, PyObject *state)
+{
+  PyObject *instdict, *slots=NULL;
+
+  if (PyTuple_Check(state))
+    {
+      if (! PyArg_ParseTuple(state, "OO", &state, &slots))
+        return NULL;
+    }
+
+  if (state != Py_None)
+    {
+      instdict = getdict(self);
+      if (instdict != NULL)
+        {
+          PyDict_Clear(instdict);
+          if (PyDict_Update(instdict, state) < 0)
+            return NULL;
+        }
+      else if (Base_setattrs_from_dict(self, state) < 0)
+        return NULL;
+    }
+
+  if (slots != NULL && Base_setattrs_from_dict(self, slots) < 0)
+    return NULL;
+
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static PyObject *
+Base___getnewargs__(PyObject *self)
+{
+  return PyTuple_New(0);
+}
+
+static PyObject *
+Base___reduce__(PyObject *self)
+{
+  PyObject *args=NULL, *bargs=0, *state;
+  int l, i;
+  
+  bargs = PyObject_CallMethodObjArgs(self, str__getnewargs__, NULL);
+  if (bargs == NULL)
+    return NULL;
+
+  l = PyTuple_Size(bargs);
+  if (l < 0)
+    goto end;
+
+  args = PyTuple_New(l+1);
+  if (args == NULL)
+    goto end;
+  
+  Py_INCREF(self->ob_type);
+  PyTuple_SET_ITEM(args, 0, OBJECT(self->ob_type));
+  for (i = 0; i < l; i++)
+    {
+      Py_INCREF(PyTuple_GET_ITEM(bargs, i));
+      PyTuple_SET_ITEM(args, i+1, PyTuple_GET_ITEM(bargs, i));
+    }
+  
+  state = PyObject_CallMethodObjArgs(self, str__getstate__, NULL);
+  if (state == NULL)
+    goto end;
+
+  state = Py_BuildValue("(OON)", __newobj__, args, state);
+
+ end:
+  Py_XDECREF(bargs);
+  Py_XDECREF(args);
+
+  return state;
+}
+
+static struct PyMethodDef Base_methods[] = {
+  {"__getstate__", (PyCFunction)Base___getstate__, METH_NOARGS, 
+   "Get object's serialization state"},
+  {"__setstate__", (PyCFunction)Base___setstate__, METH_O, 
+   "Set object's serialization state"},
+  {"__getnewargs__", (PyCFunction)Base___getnewargs__, METH_NOARGS, 
+   "Get arguments to be passed to __new__ to create a new object."},
+  {"__reduce__", (PyCFunction)Base___reduce__, METH_NOARGS, 
+   "Reduce an object to constituent parts for serialization."},
+  {NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
+  };
+
+
 static EC BaseType = {
 	PyObject_HEAD_INIT(NULL)
 	/* ob_size           */ 0,
 	/* tp_name           */ "ExtensionClass."
                                 "Base",
-	/* tp_basicsize      */ 0, /* set below */
-	/* tp_itemsize       */ 0,
-	/* tp_dealloc        */ (destructor)0,
-	/* tp_print          */ (printfunc)0,
-	/* tp_getattr        */ (getattrfunc)0,
-	/* tp_setattr        */ (setattrfunc)0,
-	/* tp_compare        */ (cmpfunc)0,
-	/* tp_repr           */ (reprfunc)0,
-	/* tp_as_number      */ 0,
-	/* tp_as_sequence    */ 0,
-	/* tp_as_mapping     */ 0,
-	/* tp_hash           */ (hashfunc)0,
-	/* tp_call           */ (ternaryfunc)0,
-	/* tp_str            */ (reprfunc)0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
         /* tp_getattro       */ (getattrofunc)Base_getattro,
-        /* tp_setattro       */ (setattrofunc)0,
-        /* tp_as_buffer      */ 0,
-        /* tp_flags          */ Py_TPFLAGS_DEFAULT 
-				| Py_TPFLAGS_BASETYPE
-                                ,
-	/* tp_doc            */ "Standard ExtensionClass base type",
+        0, 0,
+        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+	"Standard ExtensionClass base type",
+        0, 0, 0, 0, 0, 0, 
+        Base_methods,
+};
+
+static EC NoInstanceDictionaryBaseType = {
+	PyObject_HEAD_INIT(NULL)
+	/* ob_size           */ 0,
+	/* tp_name           */ "ExtensionClass."
+                                "NoInstanceDictionaryBase",
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+	"Base types for subclasses without instance dictionaries",
 };
 
 static PyObject *
@@ -249,6 +452,8 @@
         return NULL;
     }
 
+  
+
   if (new_bases)
     {
       if (dict)
@@ -265,8 +470,29 @@
       Py_DECREF(new_args);
     }
   else
-    result = PyType_Type.tp_new(self, args, kw);
-  
+    {
+      result = PyType_Type.tp_new(self, args, kw);
+
+      /* We didn't have to add Base, so maybe NoInstanceDictionaryBase
+         is in the bases. We need to check if it was. If it was, we
+         need to suppress instance dictionary support. */
+      for (i = 0; i < PyTuple_GET_SIZE(bases); i++)
+        {
+          if (
+              PyObject_TypeCheck(PyTuple_GET_ITEM(bases, i), 
+                                 &ExtensionClassType)
+              &&
+              PyType_IsSubtype(TYPE(PyTuple_GET_ITEM(bases, i)), 
+                               &NoInstanceDictionaryBaseType)
+              )
+            {
+              TYPE(result)->tp_dictoffset = 0;
+              break;
+            }
+        }
+
+    }
+
   return result;
 }
 
@@ -342,7 +568,16 @@
   return NULL;
 }
 
+static PyObject *
+__basicnew__(PyObject *self)
+{
+  return PyObject_CallMethodObjArgs(self, str__new__, self, NULL);
+}
+
+
 static struct PyMethodDef EC_methods[] = {
+  {"__basicnew__", (PyCFunction)__basicnew__, METH_NOARGS, 
+   "Create a new empty object"},
   {"inheritedAttribute", (PyCFunction)inheritedAttribute, METH_O, 
    "Look up an inherited attribute"},
   {NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
@@ -463,7 +698,7 @@
 static int
 PyExtensionClass_Export_(PyObject *dict, char *name, PyTypeObject *typ)
 {
-  int ecflags;
+  int ecflags = 0;
   PyMethodDef *pure_methods = NULL, *mdef = NULL;
   PyObject *m;
 
@@ -511,13 +746,18 @@
       typ->tp_clear = NULL; 
       typ->tp_flags = Py_TPFLAGS_DEFAULT 
                     | Py_TPFLAGS_BASETYPE;
-      
+
       if (typ->tp_dealloc != NULL)
           typ->tp_new = ec_new;
     }
 
   typ->ob_type = ECExtensionClassType; 
-  typ->tp_base = ECBaseType; 
+
+  if (ecflags & EXTENSIONCLASS_NOINSTDICT_FLAG)
+    typ->tp_base = &NoInstanceDictionaryBaseType;
+  else
+    typ->tp_base = &BaseType;
+
   if (typ->tp_new == NULL)
     typ->tp_new = PyType_GenericNew; 
 
@@ -614,6 +854,23 @@
   DEFINE_STRING(__get__);
   DEFINE_STRING(__class_init__);
   DEFINE_STRING(__init__);
+  DEFINE_STRING(__slotnames__);
+  DEFINE_STRING(__getnewargs__);
+  DEFINE_STRING(__getstate__);
+  DEFINE_STRING(__new__);
+
+  m = PyImport_ImportModule("copy_reg");
+  if (m == NULL)
+    return;
+
+  copy_reg_slotnames = PyObject_GetAttrString(m, "_slotnames");
+  if (copy_reg_slotnames == NULL)
+    return;
+
+  __newobj__ = PyObject_GetAttrString(m, "__newobj__");
+  Py_DECREF(m);
+  if (__newobj__ == NULL)
+    return;
 
   PyExtensionClassCAPI = &TrueExtensionClassCAPI;
 
@@ -625,13 +882,19 @@
   /* Initialize types: */
   if (PyType_Ready(&ExtensionClassType) < 0)
     return;
+    
+  BaseType.ob_type = &ExtensionClassType;
+  BaseType.tp_base = &PyBaseObject_Type;
+  BaseType.tp_new = PyType_GenericNew;
   
+  if (PyType_Ready(&BaseType) < 0)
+    return;
+    
+  NoInstanceDictionaryBaseType.ob_type = &ExtensionClassType;
+  NoInstanceDictionaryBaseType.tp_base = &BaseType;
+  NoInstanceDictionaryBaseType.tp_new = PyType_GenericNew;
   
-  TYPE(&BaseType)->ob_type = &ExtensionClassType;
-  TYPE(&BaseType)->tp_base = &PyBaseObject_Type;
-  TYPE(&BaseType)->tp_new = PyType_GenericNew;
-  
-  if (PyType_Ready(TYPE(&BaseType)) < 0)
+  if (PyType_Ready(&NoInstanceDictionaryBaseType) < 0)
     return;
   
   /* Create the module and add the functions */
@@ -650,6 +913,9 @@
                          (PyObject *)&ExtensionClassType) < 0)
     return;
   if (PyModule_AddObject(m, "Base", (PyObject *)&BaseType) < 0)
+    return;
+  if (PyModule_AddObject(m, "NoInstanceDictionaryBase", 
+                         (PyObject *)&NoInstanceDictionaryBaseType) < 0)
     return;
 }
 


=== Zope/lib/python/ExtensionClass/__init__.py 1.1.2.2 => 1.1.2.3 ===
--- Zope/lib/python/ExtensionClass/__init__.py:1.1.2.2	Thu Oct 23 09:03:04 2003
+++ Zope/lib/python/ExtensionClass/__init__.py	Tue Oct 28 13:46:51 2003
@@ -13,6 +13,92 @@
 ##############################################################################
 """ExtensionClass
 
+Extension Class exists to support types derived from the old ExtensionType
+meta-class that preceeded Python 2.2 and new-style classes.
+
+As a meta-class, ExtensionClass provides the following features:
+
+- Support for a class initialiser:
+
+  >>> from ExtensionClass import ExtensionClass, Base
+
+  >>> class C(Base):
+  ...   def __class_init__(self):
+  ...      print 'class init called'
+  ...      print self.__name__
+  ...   def bar(self):
+  ...      return 'bar called'
+  class init called
+  C
+  >>> c = C()
+  >>> int(c.__class__ is C)
+  1
+  >>> int(c.__class__ is type(c))
+  1
+
+- Making sure that every instance of the meta-class has Base as a base class:
+
+  >>> class X:
+  ...     __metaclass__ = ExtensionClass
+
+  >>> Base in X.__mro__
+  1
+
+- Provide an inheritedAttribute method for looking up attributes in
+  base classes:
+
+  >>> class C2(C):
+  ...   def bar(*a):
+  ...      return C2.inheritedAttribute('bar')(*a), 42
+  class init called
+  C2
+  >>> o = C2()
+  >>> o.bar()
+  ('bar called', 42)
+
+  This is for compatability with old code. New code should use super
+  instead.   
+
+The base class, Base, exists mainly to support the __of__ protocol.
+The __of__ protocol is similar to __get__ except that __of__ is called
+when an implementor is retrieved from an instance as well as from a
+class:
+
+>>> class O(Base):
+...   def __of__(*a):
+...      return a
+ 
+>>> o1 = O()
+>>> o2 = O()
+>>> C.o1 = o1
+>>> c.o2 = o2
+>>> c.o1 == (o1, c)
+1
+>>> C.o1 == o1
+1
+>>> int(c.o2 == (o2, c))
+1
+
+We accomplish this by making a class that implements __of__ a
+descriptor and treating all descriptor ExtensionClasses this way. That
+is, if an extension class is a descriptor, it's __get__ method will be
+called even when it is retrieved from an instance.
+
+>>> class O(Base):
+...   def __get__(*a):
+...      return a
+... 
+>>> o1 = O()
+>>> o2 = O()
+>>> C.o1 = o1
+>>> c.o2 = o2
+>>> int(c.o1 == (o1, c, type(c)))
+1
+>>> int(C.o1 == (o1, None, type(c)))
+1
+>>> int(c.o2 == (o2, c, type(c)))
+1
+  
 $Id$
 """
 


=== Zope/lib/python/ExtensionClass/tests.py 1.1.2.1 => 1.1.2.2 ===
--- Zope/lib/python/ExtensionClass/tests.py:1.1.2.1	Thu Oct 23 09:03:04 2003
+++ Zope/lib/python/ExtensionClass/tests.py	Tue Oct 28 13:46:51 2003
@@ -16,83 +16,59 @@
 $Id$
 """
 
-from doctest import DocTestSuite
-import unittest
+from ExtensionClass import *
+import pickle
 
-def test_basic():
-    """
-    >>> from ExtensionClass import *
 
-    Test creating basic classes and objects and test that class_init is called
+def print_dict(d):
+    d = d.items()
+    d.sort()
+    print '{%s}' % (', '.join(
+        [('%r: %r' % (k, v)) for (k, v) in d]
+        ))
 
-    >>> class C(Base):
-    ...   def __class_init__(self):
-    ...      print 'class init called'
-    ...      print self.__name__
-    ...   def bar(self): pass
-    ... 
-    class init called
-    C
-    >>> c = C()
-    >>> int(c.__class__ is C)
-    1
-    >>> int(c.__class__ is type(c))
-    1
+def test_mixing():
+    """Test working with a classic class
 
-    Test that __of__ works
+    >>> class Classic: 
+    ...   def x(self): 
+    ...     return 42
 
     >>> class O(Base):
     ...   def __of__(*a):
     ...      return a
-    ... 
-    >>> o1 = O()
-    >>> o2 = O()
-    >>> C.o1 = o1
-    >>> c.o2 = o2
-    >>> int(c.o1 == (o1, c))
-    1
-    >>> int(c.o2 == (o2, c))
-    1
-
-    Test inherited attribute:
-
-    >>> class O2(O):
-    ...   def __of__(*a):
-    ...      return O2.inheritedAttribute('__of__')(*a), 42
-    ... 
-    >>> o2 = O2()
-    >>> c.o2 = o2
-    >>> int(c.o2 == ((o2, c), 42))
-    1
-
-    Test working with a classic class
 
-    >>> class Classic: 
-    ...   def x(self): 
-    ...     return 42
-    ... 
     >>> class O2(Classic, O):
     ...   def __of__(*a):
     ...      return (O2.inheritedAttribute('__of__')(*a), 
     ...              O2.inheritedAttribute('x')(a[0]))
-    ... 
+
+    >>> class C(Base):
+    ...   def __class_init__(self):
+    ...      print 'class init called'
+    ...      print self.__name__
+    ...   def bar(self):
+    ...      return 'bar called'
+    class init called
+    C
+
+    >>> c = C()
     >>> o2 = O2()
     >>> c.o2 = o2
     >>> int(c.o2 == ((o2, c), 42))
     1
 
-
-    Test working with a ew style
+    Test working with a new style
 
     >>> class Modern(object): 
     ...   def x(self): 
     ...     return 42
-    ... 
+
     >>> class O2(Modern, O):
     ...   def __of__(*a):
     ...      return (O2.inheritedAttribute('__of__')(*a), 
     ...              O2.inheritedAttribute('x')(a[0]))
-    ... 
+
     >>> o2 = O2()
     >>> c.o2 = o2
     >>> int(c.o2 == ((o2, c), 42))
@@ -102,7 +78,6 @@
 
 def test_class_creation_under_stress():
     """
-    >>> from ExtensionClass import Base
     >>> for i in range(100): 
     ...   class B(Base):
     ...     print i,
@@ -122,8 +97,6 @@
 def old_test_add():
     """test_add.py from old EC
     
-    >>> from ExtensionClass import Base
-
     >>> class foo(Base):
     ...     def __add__(self,other): print 'add called'
 
@@ -142,7 +115,6 @@
     Will it also fix this particularity of ExtensionClass:
     
     
-    >>> from ExtensionClass import Base
     >>> class A(Base):
     ...   def foo(self):
     ...     self.gee
@@ -164,8 +136,258 @@
     raise AttributeError.
     """
 
+def test_NoInstanceDictionaryBase():
+    """
+    >>> class B(NoInstanceDictionaryBase): pass
+    ... 
+    >>> B().__dict__
+    Traceback (most recent call last):
+    ...
+    AttributeError: This object has no __dict__
+    >>> class B(NoInstanceDictionaryBase): 
+    ...   __slots__ = ('a', 'b')
+    ... 
+    >>> class BB(B): pass
+    ... 
+    >>> b = BB()
+    >>> b.__dict__
+    Traceback (most recent call last):
+    ...
+    AttributeError: This object has no __dict__
+    >>> b.a = 1
+    >>> b.b = 2
+    >>> b.a
+    1
+    >>> b.b
+    2
+    
+    """
+
+def test__basicnew__():
+    """
+    >>> x = Simple.__basicnew__()
+    >>> x.__dict__
+    {}
+    """
+
+class Simple(Base):
+    def __init__(self, name, **kw):
+        self.__name__ = name
+        self.__dict__.update(kw)
+    def __cmp__(self, other):
+        return cmp((self.__class__,  self.__dict__ ),
+                   (other.__class__, other.__dict__))
+
+def test_basic_pickling():
+    """
+    >>> x = Simple('x', aaa=1, bbb='foo')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> print_dict(x.__getstate__())
+    {'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
+    
+    >>> f, (c,), state = x.__reduce__()
+    >>> f.__name__
+    '__newobj__'
+    >>> f.__module__
+    'copy_reg'
+    >>> c.__name__
+    'Simple'
+    
+    >>> print_dict(state)
+    {'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.__setstate__({'z': 1})
+    >>> x.__dict__
+    {'z': 1}
+
+    """
+
+class Custom(Simple):
+
+    def __new__(cls, x, y):
+        r = Base.__new__(cls)
+        r.x, r.y = x, y
+        return r
+
+    def __init__(self, x, y):
+        self.a = 42
+
+    def __getnewargs__(self):
+        return self.x, self.y
+
+    def __getstate__(self):
+        return self.a
+
+    def __setstate__(self, a):
+        self.a = a
+
+
+def test_pickling_w_overrides():
+    """
+    >>> x = Custom('x', 'y')
+    >>> x.a = 99
+
+    >>> (f, (c, ax, ay), a) = x.__reduce__()
+    >>> f.__name__
+    '__newobj__'
+    >>> f.__module__
+    'copy_reg'
+    >>> c.__name__
+    'Custom'
+    >>> ax, ay, a
+    ('x', 'y', 99)
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+    
+    """
+
+class Slotted(Base):
+    __slots__ = 's1', 's2'
+    def __init__(self, s1, s2):
+        self.s1, self.s2 = s1, s2
+
+class SubSlotted(Slotted):
+    __slots__ = 's3', 's4'
+    def __init__(self, s1, s2, s3):
+        self.s1, self.s2, self.s3 = s1, s2, s3
+        
+    def __cmp__(self, other):
+        return cmp(
+            (self.__class__,
+             self.s1, self.s2, self.s3, getattr(self, 's4', 0)),
+            (other.__class__,
+             other.s1, other.s2, other.s3, getattr(other, 's4', 0)),
+            )
+
+
+def test_pickling_w_slots_only():
+    """
+    >>> x = SubSlotted('x', 'y', 'z')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> d
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.s4 = 'spam'
+    
+    >>> d, s = x.__getstate__()
+    >>> d
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    """
+
+class SubSubSlotted(SubSlotted):
+
+    def __init__(self, s1, s2, s3, **kw):
+        SubSlotted.__init__(self, s1, s2, s3)
+        self.__dict__.update(kw)
+        
+    def __cmp__(self, other):
+        return cmp(
+            (self.__class__,
+             self.s1, self.s2, self.s3, getattr(self, 's4', 0),
+             self.__dict__ ),
+            (other.__class__,
+             other.s1, other.s2, other.s3, getattr(other, 's4', 0),
+             other.__dict__ ),
+            )
+
+def test_pickling_w_slots():
+    """
+    >>> x = SubSubSlotted('x', 'y', 'z', aaa=1, bbb='foo')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {'aaa': 1, 'bbb': 'foo'}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.s4 = 'spam'
+    
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {'aaa': 1, 'bbb': 'foo'}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
+
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    """
+    
+
+        
+
+from doctest import DocTestSuite
+import unittest
 
 def test_suite():
-    return DocTestSuite()
+    return unittest.TestSuite((
+        DocTestSuite('ExtensionClass'),
+        DocTestSuite(),
+        ))
 
 if __name__ == '__main__': unittest.main()
+
+
+




More information about the Zope-Checkins mailing list