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

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:45:33 EST 2003


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

Added Files:
	ExtensionClass.h _ExtensionClass.c __init__.py setup.py 
	tests.py 
Log Message:
Reimplemented extension classes based on new-style classes.


=== Zope/lib/python/ExtensionClass/ExtensionClass.h 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:33 2003
+++ Zope/lib/python/ExtensionClass/ExtensionClass.h	Fri Nov 28 11:45:02 2003
@@ -0,0 +1,267 @@
+/*****************************************************************************
+
+  Copyright (c) 1996-2002 Zope Corporation and Contributors.
+  All Rights Reserved.
+
+  This software is subject to the provisions of the Zope Public License,
+  Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+  FOR A PARTICULAR PURPOSE
+
+ ****************************************************************************/
+
+/*
+
+  $Id$
+
+  Extension Class Definitions
+
+  Implementing base extension classes
+  
+    A base extension class is implemented in much the same way that an
+    extension type is implemented, except:
+  
+    - The include file, 'ExtensionClass.h', must be included.
+ 
+    - The type structure is declared to be of type
+	  'PyExtensionClass', rather than of type 'PyTypeObject'.
+ 
+    - The type structure has an additional member that must be defined
+	  after the documentation string.  This extra member is a method chain
+	  ('PyMethodChain') containing a linked list of method definition
+	  ('PyMethodDef') lists.  Method chains can be used to implement
+	  method inheritance in C.  Most extensions don't use method chains,
+	  but simply define method lists, which are null-terminated arrays
+	  of method definitions.  A macro, 'METHOD_CHAIN' is defined in
+	  'ExtensionClass.h' that converts a method list to a method chain.
+	  (See the example below.)
+  
+    - Module functions that create new instances must be replaced by an
+	  '__init__' method that initializes, but does not create storage for 
+	  instances.
+  
+    - The extension class must be initialized and exported to the module
+	  with::
+  
+	      PyExtensionClass_Export(d,"name",type);
+  
+	  where 'name' is the module name and 'type' is the extension class
+	  type object.
+  
+    Attribute lookup
+  
+	  Attribute lookup is performed by calling the base extension class
+	  'getattr' operation for the base extension class that includes C
+	  data, or for the first base extension class, if none of the base
+	  extension classes include C data.  'ExtensionClass.h' defines a
+	  macro 'Py_FindAttrString' that can be used to find an object's
+	  attributes that are stored in the object's instance dictionary or
+	  in the object's class or base classes::
+  
+	     v = Py_FindAttrString(self,name);
+  
+	  In addition, a macro is provided that replaces 'Py_FindMethod'
+	  calls with logic to perform the same sort of lookup that is
+	  provided by 'Py_FindAttrString'.
+  
+    Linking
+  
+	  The extension class mechanism was designed to be useful with
+	  dynamically linked extension modules.  Modules that implement
+	  extension classes do not have to be linked against an extension
+	  class library.  The macro 'PyExtensionClass_Export' imports the
+	  'ExtensionClass' module and uses objects imported from this module
+	  to initialize an extension class with necessary behavior.
+
+*/
+
+#ifndef EXTENSIONCLASS_H
+#define EXTENSIONCLASS_H
+
+#include "Python.h"
+#include "import.h"
+
+/* Declarations for objects of type ExtensionClass */
+
+#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);
+  PyObject *(*PyECMethod_New_)(PyObject *callable, PyObject *inst);
+  PyExtensionClass *ECBaseType_;
+  PyExtensionClass *ECExtensionClassType_;
+}  *PyExtensionClassCAPI = NULL;
+
+#define ECBaseType (PyExtensionClassCAPI->ECBaseType_)
+#define ECExtensionClassType (PyExtensionClassCAPI->ECExtensionClassType_)
+
+/* Following are macros that are needed or useful for defining extension
+   classes:
+   */
+
+/* This macro redefines Py_FindMethod to do attribute for an attribute
+   name given by a C string lookup using extension class meta-data.
+   This is used by older getattr implementations.
+
+   This macro is used in base class implementations of tp_getattr to
+   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.
+   
+   Note that in Python 1.4, a getattr operation may be provided that
+   uses an object argument. Classes that support this new operation
+   should use Py_FindAttr.
+   */
+
+#define EC_findiattrs (PyExtensionClassCAPI->EC_findiattrs_)
+
+#define Py_FindMethod(M,SELF,NAME) (EC_findiattrs((SELF),(NAME)))
+
+/* Do method or attribute lookup for an attribute name given by a C
+   string using extension class meta-data.
+
+   This macro is used in base class implementations of tp_getattro to
+   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.
+   
+   Note that in Python 1.4, a getattr operation may be provided that
+   uses an object argument. Classes that support this new operation
+   should use Py_FindAttr.
+   */
+#define Py_FindAttrString(SELF,NAME)  (EC_findiattrs((SELF),(NAME)))
+
+/* Do method or attribute lookup using extension class meta-data.
+
+   This macro is used in base class implementations of tp_getattr to
+   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 (ECBaseType->tp_getattro)
+
+/* Do attribute assignment for an attribute.
+
+   This macro is used in base class implementations of tp_setattro to
+   set attributes that are not managed by the base type directly.  The
+   macro is generally used to assign attributes after other attribute
+   attempts to assign attributes have failed.
+   */
+#define PyEC_SetAttr(SELF,NAME,V) (ECBaseType->tp_setattro(SELF, NAME, V))
+
+
+/* Convert a method list to a method chain.  */
+#define METHOD_CHAIN(DEF) (traverseproc)(DEF)
+
+/* The following macro checks whether a type is an extension class: */
+#define PyExtensionClass_Check(TYPE) \
+  (((PyObject*)(TYPE))->ob_type == ECExtensionClassType)
+
+/* The following macro checks whether an instance is an extension instance: */
+#define PyExtensionInstance_Check(INST) \
+  (((PyObject*)(INST))->ob_type->ob_type == ECExtensionClassType)
+
+#define CHECK_FOR_ERRORS(MESS) 
+
+/* The following macro can be used to define an extension base class
+   that only provides method and that is used as a pure mix-in class. */
+#define PURE_MIXIN_CLASS(NAME,DOC,METHODS) \
+static PyExtensionClass NAME ## Type = { PyObject_HEAD_INIT(NULL) 0, # NAME, \
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+   0 , DOC, (traverseproc)METHODS, }
+
+/* The following macros provide limited access to extension-class
+   method facilities. */
+
+/* Test for an ExtensionClass method: */
+#define PyECMethod_Check(O) PyMethod_Check((O))
+
+/* Create a method object that wraps a callable object and an
+   instance. Note that if the callable object is an extension class
+   method, then the new method will wrap the callable object that is
+   wrapped by the extension class method.  Also note that if the
+   callable object is an extension class method with a reference
+   count of 1, then the callable object will be rebound to the
+   instance and returned with an incremented reference count.
+   */
+#define PyECMethod_New(CALLABLE, INST) \
+  PyExtensionClassCAPI->PyECMethod_New_((CALLABLE),(INST))
+
+/* Return the instance that is bound by an extension class method. */
+#define PyECMethod_Self(M) \
+(PyMethod_Check((M)) ? ((PyMethodObject*)(M))->im_self : NULL)
+
+/* Check whether an object has an __of__ method for returning itself
+   in the context of it's container. */
+#define has__of__(O) ((O)->ob_type->ob_type == ECExtensionClassType \
+                      && (O)->ob_type->tp_descr_get != NULL)
+
+/* The following macros are used to check whether an instance
+   or a class' instanses have instance dictionaries: */
+#define HasInstDict(O) (_PyObject_GetDictPtr(O) != NULL)
+
+#define ClassHasInstDict(C) ((C)->tp_dictoffset > 0))
+
+/* Get an object's instance dictionary.  Use with caution */
+#define INSTANCE_DICT(inst) (_PyObject_GetDictPtr(O))
+
+/* Test whether an ExtensionClass, S, is a subclass of ExtensionClass C. */
+#define ExtensionClassSubclass_Check(S,C) PyType_IsSubtype((S), (C))
+
+/* Test whether an ExtensionClass instance , I, is a subclass of 
+   ExtensionClass C. */
+#define ExtensionClassSubclassInstance_Check(I,C) PyObject_TypeCheck((I), (C))
+
+
+/* Export an Extension Base class in a given module dictionary with a
+   given name and ExtensionClass structure.
+   */
+
+#define PyExtensionClass_Export(D,N,T) \
+  if (! ExtensionClassImported || \
+      PyExtensionClassCAPI->PyExtensionClass_Export_((D),(N),&(T)) < 0) return;
+
+
+#define ExtensionClassImported \
+  ((PyExtensionClassCAPI != NULL) || \
+   (PyExtensionClassCAPI = PyCObject_Import("ExtensionClass","CAPI2")))
+
+
+/* These are being overridded to use tp_free when used with
+   new-style classes. This is to allow old extention-class code
+   to work.
+*/
+
+#undef PyMem_DEL
+#undef PyObject_DEL
+
+#define PyMem_DEL(O)                                   \
+  if (((O)->ob_type->tp_flags & Py_TPFLAGS_HAVE_CLASS) \
+      && ((O)->ob_type->tp_free != NULL))              \
+    (O)->ob_type->tp_free((PyObject*)(O));             \
+  else                                                 \
+    PyObject_FREE((O));
+
+#define PyObject_DEL(O) PyMem_DEL(O)
+
+#endif /* EXTENSIONCLASS_H */


=== Zope/lib/python/ExtensionClass/_ExtensionClass.c 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:33 2003
+++ Zope/lib/python/ExtensionClass/_ExtensionClass.c	Fri Nov 28 11:45:02 2003
@@ -0,0 +1,897 @@
+/*
+
+ Copyright (c) 2003 Zope Corporation and Contributors.
+ All Rights Reserved.
+
+ This software is subject to the provisions of the Zope Public License,
+ Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+ WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+ FOR A PARTICULAR PURPOSE.
+
+*/
+static char _extensionclass_module_documentation[] = 
+"ExtensionClass\n"
+"\n"
+"$Id$\n"
+;
+
+#include "ExtensionClass.h"
+
+#define EC PyTypeObject
+
+static PyObject *str__of__, *str__get__, *str__class_init__, *str__init__;
+static PyObject *str__bases__, *str__mro__, *str__new__;
+
+#define OBJECT(O) ((PyObject *)(O))
+#define TYPE(O) ((PyTypeObject *)(O))
+
+extern PyTypeObject ExtensionClassType;
+extern PyTypeObject BaseType;
+
+static PyObject *
+of_get(PyObject *self, PyObject *inst, PyObject *cls)
+{
+  /* Descriptor slot function that calls __of__ */
+  if (inst && PyExtensionInstance_Check(inst))
+    return PyObject_CallMethodObjArgs(self, str__of__, inst, NULL);
+
+  Py_INCREF(self);
+  return self;
+}
+
+PyObject *
+Base_getattro(PyObject *obj, PyObject *name)
+{
+  /* This is a modified copy of PyObject_GenericGetAttr.
+     See the change note below. */
+
+	PyTypeObject *tp = obj->ob_type;
+	PyObject *descr = NULL;
+	PyObject *res = NULL;
+	descrgetfunc f;
+	long dictoffset;
+	PyObject **dictptr;
+
+	if (!PyString_Check(name)){
+#ifdef Py_USING_UNICODE
+		/* The Unicode to string conversion is done here because the
+		   existing tp_setattro slots expect a string object as name
+		   and we wouldn't want to break those. */
+		if (PyUnicode_Check(name)) {
+			name = PyUnicode_AsEncodedString(name, NULL, NULL);
+			if (name == NULL)
+				return NULL;
+		}
+		else
+#endif
+		{
+			PyErr_SetString(PyExc_TypeError,
+					"attribute name must be string");
+			return NULL;
+		}
+	}
+	else
+		Py_INCREF(name);
+
+	if (tp->tp_dict == NULL) {
+		if (PyType_Ready(tp) < 0)
+			goto done;
+	}
+
+	/* Inline _PyType_Lookup */
+	{
+		int i, n;
+		PyObject *mro, *base, *dict;
+
+		/* Look in tp_dict of types in MRO */
+		mro = tp->tp_mro;
+		assert(mro != NULL);
+		assert(PyTuple_Check(mro));
+		n = PyTuple_GET_SIZE(mro);
+		for (i = 0; i < n; i++) {
+			base = PyTuple_GET_ITEM(mro, i);
+			if (PyClass_Check(base))
+				dict = ((PyClassObject *)base)->cl_dict;
+			else {
+				assert(PyType_Check(base));
+				dict = ((PyTypeObject *)base)->tp_dict;
+			}
+			assert(dict && PyDict_Check(dict));
+			descr = PyDict_GetItem(dict, name);
+			if (descr != NULL)
+				break;
+		}
+	}
+
+	f = NULL;
+	if (descr != NULL &&
+	    PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) {
+		f = descr->ob_type->tp_descr_get;
+		if (f != NULL && PyDescr_IsData(descr)) {
+			res = f(descr, obj, (PyObject *)obj->ob_type);
+			goto done;
+		}
+	}
+
+	/* Inline _PyObject_GetDictPtr */
+	dictoffset = tp->tp_dictoffset;
+	if (dictoffset != 0) {
+		PyObject *dict;
+		if (dictoffset < 0) {
+			int tsize;
+			size_t size;
+
+			tsize = ((PyVarObject *)obj)->ob_size;
+			if (tsize < 0)
+				tsize = -tsize;
+			size = _PyObject_VAR_SIZE(tp, tsize);
+
+			dictoffset += (long)size;
+			assert(dictoffset > 0);
+			assert(dictoffset % SIZEOF_VOID_P == 0);
+		}
+		dictptr = (PyObject **) ((char *)obj + dictoffset);
+		dict = *dictptr;
+		if (dict != NULL) {
+			res = PyDict_GetItem(dict, name);
+			if (res != NULL) {
+
+                          /* CHANGED!
+                             If the tp_descr_get of res is of_get, 
+                             then call it. */
+
+                          if (res->ob_type->ob_type == &ExtensionClassType
+                              && res->ob_type->tp_descr_get != NULL)
+                            res = res->ob_type->tp_descr_get(
+                                                 res, obj, 
+                                                 OBJECT(obj->ob_type));
+                          else
+                            Py_INCREF(res);
+                          goto done;
+			}
+		}
+	}
+
+	if (f != NULL) {
+		res = f(descr, obj, (PyObject *)obj->ob_type);
+		goto done;
+	}
+
+	if (descr != NULL) {
+		Py_INCREF(descr);
+		res = descr;
+		goto done;
+	}
+
+        /* CHANGED: Just use the name. Don't format. */
+        PyErr_SetObject(PyExc_AttributeError, name);
+  done:
+	Py_DECREF(name);
+	return res;
+}
+
+#include "pickle/pickle.c"
+
+static struct PyMethodDef Base_methods[] = {
+  PICKLE_METHODS
+  {NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
+  };
+
+
+static EC BaseType = {
+	PyObject_HEAD_INIT(NULL)
+	/* ob_size           */ 0,
+	/* tp_name           */ "ExtensionClass."
+                                "Base",
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+        /* tp_getattro       */ (getattrofunc)Base_getattro,
+        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 *
+EC_new(PyTypeObject *self, PyObject *args, PyObject *kw)
+{
+  PyObject *name, *bases=NULL, *dict=NULL;
+  PyObject *new_bases=NULL, *new_args, *result;
+  int have_base = 0, i;
+
+  if (kw && PyObject_IsTrue(kw))
+    {
+      PyErr_SetString(PyExc_TypeError, 
+                      "Keyword arguments are not supported");
+        return NULL;
+    }
+
+  if (!PyArg_ParseTuple(args, "O|O!O!", &name,
+                        &PyTuple_Type, &bases, &PyDict_Type, &dict))
+    return NULL;
+
+  /* Make sure Base is in bases */
+  if (bases)
+    {
+      for (i = 0; i < PyTuple_GET_SIZE(bases); i++)
+        {
+          if (PyObject_TypeCheck(PyTuple_GET_ITEM(bases, i), 
+                                 &ExtensionClassType))
+            {
+              have_base = 1;
+              break;
+            }
+        }
+      if (! have_base)
+        {
+          new_bases = PyTuple_New(PyTuple_GET_SIZE(bases) + 1);
+          if (new_bases == NULL)
+            return NULL;
+          for (i = 0; i < PyTuple_GET_SIZE(bases); i++)
+            {
+              Py_XINCREF(PyTuple_GET_ITEM(bases, i));
+              PyTuple_SET_ITEM(new_bases, i, PyTuple_GET_ITEM(bases, i));
+            }
+          Py_INCREF(OBJECT(&BaseType));
+          PyTuple_SET_ITEM(new_bases, PyTuple_GET_SIZE(bases), 
+                           OBJECT(&BaseType));
+        }
+    }
+  else
+    {
+      new_bases = Py_BuildValue("(O)", &BaseType);
+      if (new_bases == NULL)
+        return NULL;
+    }
+
+  
+
+  if (new_bases)
+    {
+      if (dict)
+        new_args = Py_BuildValue("OOO", name, new_bases, dict);
+      else
+        new_args = Py_BuildValue("OO", name, new_bases);
+
+      Py_DECREF(new_bases);
+
+      if (new_args == NULL)
+        return NULL;
+
+      result = PyType_Type.tp_new(self, new_args, kw);
+      Py_DECREF(new_args);
+    }
+  else
+    {
+      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;
+}
+
+static int
+EC_init(PyTypeObject *self, PyObject *args, PyObject *kw)
+{
+  PyObject *__class_init__, *__of__, *r;
+
+  if (PyType_Type.tp_init(OBJECT(self), args, kw) < 0) 
+    return -1; 
+
+  /* set up __get__, if necessary */
+  if (self->tp_descr_get != of_get)
+    {
+      __of__ = PyObject_GetAttr(OBJECT(self), str__of__);
+      if (__of__)
+        {
+          Py_DECREF(__of__);
+          if (self->tp_descr_get)
+            {
+              PyErr_SetString(PyExc_TypeError,
+                              "Can't mix __of__ and descriptors");
+              return -1;
+            }
+          self->tp_descr_get = of_get;
+        }
+      else
+        PyErr_Clear();
+    }
+
+  /* Call __class_init__ */
+  __class_init__ = PyObject_GetAttr(OBJECT(self), str__class_init__);
+  if (__class_init__ == NULL)
+    {
+      PyErr_Clear();
+      return 0;
+    }
+
+  if (! (PyMethod_Check(__class_init__) 
+         && PyMethod_GET_FUNCTION(__class_init__)
+         )
+      )
+    {
+      Py_DECREF(__class_init__);
+      PyErr_SetString(PyExc_TypeError, "Invalid type for __class_init__");
+      return -1;
+    }
+
+  r = PyObject_CallFunctionObjArgs(PyMethod_GET_FUNCTION(__class_init__),
+                                   OBJECT(self), NULL);
+  Py_DECREF(__class_init__);
+  if (! r)
+    return -1;
+  Py_DECREF(r);
+  
+  return 0;
+}
+
+static int
+EC_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
+{
+  /* We want to allow setting attributes of builti-in types, because
+     EC did in the past and there's code that relies on it.
+
+     We can't really set slots though, but I don't think we need to.
+     There's no good way to spot slots.  We could use a lame rule like
+     names that begin and end with __s and have just 4 _s smell too
+     much like slots.
+
+
+  */
+  if (! (type->tp_flags & Py_TPFLAGS_HEAPTYPE)) 
+    {
+      char *cname;
+      int l;
+
+      cname = PyString_AsString(name);
+      if (cname == NULL)
+        return -1;
+      l = PyString_GET_SIZE(name);
+      if (l > 4 
+          && cname[0] == '_' && cname[1] == '_'
+          && cname[l-1] == '_' && cname[l-2] == '_'
+          )
+        {
+          char *c;
+          
+          c = strchr(cname+2, '_');
+          if (c != NULL && (c - cname) >= (l-2))
+            {
+              PyErr_Format
+                (PyExc_TypeError,
+                 "can't set attributes of built-in/extension type '%s' if the "
+                 "attribute name begins and ends with __ and contains only "
+                 "4 _ characters",
+                 type->tp_name
+                 );
+              return -1;
+            }
+        }
+      
+      return PyObject_GenericSetAttr(OBJECT(type), name, value);
+    }
+  return PyType_Type.tp_setattro(OBJECT(type), name, value);
+}
+
+
+static PyObject *
+inheritedAttribute(PyTypeObject *self, PyObject *name)
+{
+  int i;
+  PyObject *d, *cls;
+
+  for (i = 1; i < PyTuple_GET_SIZE(self->tp_mro); i++)
+    {
+      cls = PyTuple_GET_ITEM(self->tp_mro, i);
+      if (PyType_Check(cls))
+        d = ((PyTypeObject *)cls)->tp_dict;
+      else if (PyClass_Check(cls))
+        d = ((PyClassObject *)cls)->cl_dict;
+      else
+        /* Unrecognized thing, punt */
+        d = NULL;
+      
+      if ((d == NULL) || (PyDict_GetItem(d, name) == NULL))
+        continue;
+                    
+      return PyObject_GetAttr(cls, name);
+    }
+
+  PyErr_SetObject(PyExc_AttributeError, name);
+  return NULL;
+}
+
+static PyObject *
+__basicnew__(PyObject *self)
+{
+  return PyObject_CallMethodObjArgs(self, str__new__, self, NULL);
+}
+
+static int
+append_new(PyObject *result, PyObject *v)
+{
+  int contains;
+
+  if (v == OBJECT(&BaseType) || v == OBJECT(&PyBaseObject_Type))
+    return 0;                   /* Don't add these until end */
+  contains = PySequence_Contains(result, v);
+  if (contains != 0)
+    return contains;
+  return PyList_Append(result, v);
+}
+
+static int
+copy_mro(PyObject *mro, PyObject *result)
+{
+  PyObject *base;
+  int i, l;
+
+  l = PyTuple_Size(mro);
+  if (l < 0) 
+    return -1;
+
+  for (i=0; i < l; i++)
+    {
+      base = PyTuple_GET_ITEM(mro, i);
+      if (append_new(result, base) < 0)
+        return -1;
+    }
+  return 0;
+}
+
+static int 
+copy_classic(PyObject *base, PyObject *result)
+{
+  PyObject *bases, *basebase;
+  int i, l, err=-1;
+
+  if (append_new(result, base) < 0)
+    return -1;
+
+  bases = PyObject_GetAttr(base, str__bases__);
+  if (bases == NULL)
+    return -1;
+
+  l = PyTuple_Size(bases);
+  if (l < 0) 
+    goto end;
+
+  for (i=0; i < l; i++)
+    {
+      basebase = PyTuple_GET_ITEM(bases, i);
+      if (copy_classic(basebase, result) < 0)
+        goto end;
+    }
+
+  err = 0;
+ 
+ end:
+  Py_DECREF(bases);
+  return err;
+}
+
+static PyObject *
+mro(PyTypeObject *self)
+{
+  /* Compute an MRO for a class */
+  PyObject *result, *base, *basemro, *mro=NULL;
+  int i, l, err;
+
+  result = PyList_New(0);
+  if (result == NULL)
+    return NULL;
+  if (PyList_Append(result, OBJECT(self)) < 0)
+    goto end;
+  l = PyTuple_Size(self->tp_bases);
+  if (l < 0) 
+    goto end;
+  for (i=0; i < l; i++)
+    {
+      base = PyTuple_GET_ITEM(self->tp_bases, i);
+      if (base == NULL)
+        continue;
+      basemro = PyObject_GetAttr(base, str__mro__);
+      if (basemro != NULL)
+        {
+          /* Type */
+          err = copy_mro(basemro, result);
+          Py_DECREF(basemro);
+          if (err < 0)
+            goto end;
+        }
+      else
+        {
+          PyErr_Clear();
+          if (copy_classic(base, result) < 0)
+            goto end;
+        }
+    }
+
+  if (self != &BaseType && PyList_Append(result, OBJECT(&BaseType)) < 0)
+    goto end;
+
+  if (PyList_Append(result, OBJECT(&PyBaseObject_Type)) < 0)
+    goto end;
+
+  l = PyList_GET_SIZE(result);
+  mro = PyTuple_New(l);
+  if (mro == NULL)
+    goto end;
+
+  for (i=0; i < l; i++)
+    {
+      Py_INCREF(PyList_GET_ITEM(result, i));
+      PyTuple_SET_ITEM(mro, i, PyList_GET_ITEM(result, i));
+    }
+ 
+ end:
+  Py_DECREF(result);
+  return mro;
+}
+
+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"},
+  {"mro", (PyCFunction)mro, METH_NOARGS, 
+   "Compute an mro using the 'encalsulated base' scheme"},
+  {NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
+  };
+
+
+static PyTypeObject ExtensionClassType = {
+	PyObject_HEAD_INIT(NULL)
+	/* ob_size           */ 0,
+	/* tp_name           */ "ExtensionClass."
+                                "ExtensionClass",
+	/* tp_basicsize      */ 0,
+	/* 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,
+        /* tp_getattro       */ (getattrofunc)0,
+        /* tp_setattro       */ (setattrofunc)EC_setattro,
+        /* tp_as_buffer      */ 0,
+        /* tp_flags          */ Py_TPFLAGS_DEFAULT
+                                | Py_TPFLAGS_HAVE_GC
+                                ,
+	/* tp_doc            */ "Meta-class for extension classes",
+        /* tp_traverse       */ (traverseproc)0,
+        /* tp_clear          */ (inquiry)0,
+        /* tp_richcompare    */ (richcmpfunc)0,
+        /* tp_weaklistoffset */ (long)0,
+        /* tp_iter           */ (getiterfunc)0,
+        /* tp_iternext       */ (iternextfunc)0,
+        /* tp_methods        */ EC_methods,
+        /* tp_members        */ 0,
+        /* tp_getset         */ 0,
+        /* tp_base           */ 0,
+        /* tp_dict           */ 0, /* internal use */
+        /* tp_descr_get      */ (descrgetfunc)0,
+        /* tp_descr_set      */ (descrsetfunc)0,
+        /* tp_dictoffset     */ 0,
+        /* tp_init           */ (initproc)EC_init,
+        /* tp_alloc          */ (allocfunc)0,
+        /* tp_new            */ (newfunc)EC_new,
+	/* tp_free           */ 0, /* Low-level free-mem routine */
+	/* tp_is_gc          */ (inquiry)0, /* For PyObject_IS_GC */
+};
+
+static PyObject *
+debug(PyObject *self, PyObject *o)
+{
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+/* List of methods defined in the module */
+
+static struct PyMethodDef ec_methods[] = {
+  {"debug", (PyCFunction)debug, METH_O, ""},
+  {NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
+  };
+
+
+static PyObject *
+EC_findiattrs_(PyObject *self, char *cname)
+{
+  PyObject *name, *r;
+  
+  name = PyString_FromString(cname);
+  if (name == NULL)
+    return NULL;
+  r = ECBaseType->tp_getattro(self, name);
+  Py_DECREF(name);
+  return r;
+}
+
+static PyObject *
+ec_new_for_custom_dealloc(PyTypeObject *type, PyObject *args, PyObject *kw)
+{
+  /* This is for EC's that have deallocs.  For these, we need to
+     incref the type when we create an instance, because the deallocs
+     will decref the type.
+  */
+
+  PyObject *r;
+
+  r = PyType_GenericNew(type, args, kw);
+  if (r)
+    {
+      Py_INCREF(type);
+    }
+  return r;
+}
+
+static int
+ec_init(PyObject *self, PyObject *args, PyObject *kw)
+{
+  PyObject *r, *__init__;
+
+  __init__ = PyObject_GetAttr(self, str__init__);
+  if (__init__ == NULL)
+    return -1;
+    
+  r = PyObject_Call(__init__, args, kw);
+  Py_DECREF(__init__);
+  if (r == NULL)
+    return -1;
+
+  Py_DECREF(r);
+  return 0;
+}
+
+static int
+PyExtensionClass_Export_(PyObject *dict, char *name, PyTypeObject *typ)
+{
+  int ecflags = 0;
+  PyMethodDef *pure_methods = NULL, *mdef = NULL;
+  PyObject *m;
+
+  if (typ->tp_flags == 0) 
+    { 
+      /* Old-style EC */
+
+      if (typ->tp_traverse) 
+        { 
+          /* ExtensionClasses stick there methods in the tp_traverse slot */
+          mdef = (PyMethodDef *)typ->tp_traverse;
+
+          if (typ->tp_basicsize <= sizeof(_emptyobject))
+            /* Pure mixin. We want rebindable methods */
+            pure_methods = mdef;
+          else
+            typ->tp_methods = mdef;
+
+          typ->tp_traverse = NULL; 
+
+          /* Look for __init__ method  */
+          for (; mdef->ml_name; mdef++)
+            {
+              if (strcmp(mdef->ml_name, "__init__") == 0)
+                {
+                  /* we have an old-style __init__, install a special slot */
+                  typ->tp_init = ec_init;
+                  break;
+                }
+            }
+        } 
+
+      if (typ->tp_clear)
+        {
+          /* ExtensionClasses stick there flags in the tp_clear slot */
+          ecflags = (int)(typ->tp_clear);
+
+          /* Some old-style flags were set */
+
+          if ((ecflags & EXTENSIONCLASS_BINDABLE_FLAG)
+              && typ->tp_descr_get == NULL)
+            /* We have __of__-style binding */
+            typ->tp_descr_get = of_get; 
+        }
+      typ->tp_clear = NULL; 
+      typ->tp_flags = Py_TPFLAGS_DEFAULT 
+                    | Py_TPFLAGS_BASETYPE;
+
+      if (typ->tp_dealloc != NULL)
+          typ->tp_new = ec_new_for_custom_dealloc;
+    }
+
+  typ->ob_type = ECExtensionClassType; 
+
+  if (ecflags & EXTENSIONCLASS_NOINSTDICT_FLAG)
+    typ->tp_base = &NoInstanceDictionaryBaseType;
+  else
+    typ->tp_base = &BaseType;
+
+  if (typ->tp_new == NULL)
+    typ->tp_new = PyType_GenericNew; 
+
+  if (PyType_Ready(typ) < 0) 
+    return -1; 
+
+  if (pure_methods)
+    {
+      /* We had pure methods. We want to be able to rebind these, so
+         we'll make them ordinary method wrappers around method descrs
+      */
+      for (; pure_methods->ml_name; pure_methods++)
+        {
+          m = PyDescr_NewMethod(ECBaseType, pure_methods);
+          if (! m)
+            return -1;
+          m = PyMethod_New((PyObject *)m, NULL, (PyObject *)ECBaseType);
+          if (! m)
+            return -1;
+          if (PyDict_SetItemString(typ->tp_dict, pure_methods->ml_name, m) 
+              < 0)
+            return -1;
+        }      
+    }
+  else if (mdef && mdef->ml_name)
+    {
+      /* Blast, we have to stick __init__ in the dict ourselves
+         because PyType_Ready probably stuck a wrapper for ec_init in
+         instead.
+      */
+      m = PyDescr_NewMethod(typ, mdef);
+      if (! m)
+        return -1;
+      if (PyDict_SetItemString(typ->tp_dict, mdef->ml_name, m) < 0)
+        return -1;
+    }
+
+  if (PyMapping_SetItemString(dict, name, (PyObject*)typ) < 0)  
+    return -1; 
+
+  return 0;
+}
+
+PyObject *
+PyECMethod_New_(PyObject *callable, PyObject *inst)
+{
+  if (! PyExtensionInstance_Check(inst))
+    {
+      PyErr_SetString(PyExc_TypeError, 
+                      "Can't bind non-ExtensionClass instance.");
+      return NULL;
+    }
+
+  if (PyMethod_Check(callable))
+    {
+      if (callable->ob_refcnt == 1)
+        {
+          Py_XDECREF(((PyMethodObject*)callable)->im_self);
+          Py_INCREF(inst);
+          ((PyMethodObject*)callable)->im_self = inst;
+          Py_INCREF(callable);
+          return callable;
+        }
+      else
+        return callable->ob_type->tp_descr_get(
+                   callable, inst, 
+                   ((PyMethodObject*)callable)->im_class);
+    }
+  else
+    return PyMethod_New(callable, inst, (PyObject*)(ECBaseType));
+}
+
+static struct ExtensionClassCAPIstruct
+TrueExtensionClassCAPI = {
+  EC_findiattrs_,
+  PyExtensionClass_Export_,
+  PyECMethod_New_,
+  &BaseType,
+  &ExtensionClassType,
+};
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_ExtensionClass(void)
+{
+  PyObject *m, *s;
+
+  if (pickle_setup() < 0)
+    return;
+
+#define DEFINE_STRING(S) \
+  if(! (str ## S = PyString_FromString(# S))) return
+
+  DEFINE_STRING(__of__);
+  DEFINE_STRING(__get__);
+  DEFINE_STRING(__class_init__);
+  DEFINE_STRING(__init__);
+  DEFINE_STRING(__bases__);
+  DEFINE_STRING(__mro__);
+  DEFINE_STRING(__new__);
+#undef DEFINE_STRING
+
+  PyExtensionClassCAPI = &TrueExtensionClassCAPI;
+
+  ExtensionClassType.ob_type = &PyType_Type;
+  ExtensionClassType.tp_base = &PyType_Type;
+  ExtensionClassType.tp_traverse = PyType_Type.tp_traverse;
+  ExtensionClassType.tp_clear = PyType_Type.tp_clear;
+  
+  /* 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;
+  
+  if (PyType_Ready(&NoInstanceDictionaryBaseType) < 0)
+    return;
+  
+  /* Create the module and add the functions */
+  m = Py_InitModule3("_ExtensionClass", ec_methods,
+                     _extensionclass_module_documentation);
+
+  if (m == NULL)
+    return;
+
+  s = PyCObject_FromVoidPtr(PyExtensionClassCAPI, NULL);
+  if (PyModule_AddObject(m, "CAPI2", s) < 0)
+    return;
+        
+  /* Add types: */
+  if (PyModule_AddObject(m, "ExtensionClass", 
+                         (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 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:33 2003
+++ Zope/lib/python/ExtensionClass/__init__.py	Fri Nov 28 11:45:02 2003
@@ -0,0 +1,105 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""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$
+"""
+
+from _ExtensionClass import *


=== Zope/lib/python/ExtensionClass/setup.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:33 2003
+++ Zope/lib/python/ExtensionClass/setup.py	Fri Nov 28 11:45:02 2003
@@ -0,0 +1,8 @@
+from distutils.core import setup, Extension
+setup(name="ExtensionClass", version="2.0",
+      ext_modules=[
+         Extension("_ExtensionClass", ["_ExtensionClass.c"],
+                   depends = ["ExtensionClass.h", "pickle/pickle.c"],
+                   ),
+         ])
+


=== Zope/lib/python/ExtensionClass/tests.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:33 2003
+++ Zope/lib/python/ExtensionClass/tests.py	Fri Nov 28 11:45:02 2003
@@ -0,0 +1,705 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+from ExtensionClass import *
+import pickle
+
+
+def print_dict(d):
+    d = d.items()
+    d.sort()
+    print '{%s}' % (', '.join(
+        [('%r: %r' % (k, v)) for (k, v) in d]
+        ))
+
+def test_mixing():
+    """Test working with a classic class
+
+    >>> class Classic: 
+    ...   def x(self): 
+    ...     return 42
+
+    >>> class O(Base):
+    ...   def __of__(*a):
+    ...      return a
+
+    >>> 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 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))
+    1
+
+    """
+
+def test_class_creation_under_stress():
+    """
+    >>> for i in range(100): 
+    ...   class B(Base):
+    ...     print i,
+    ...     if i and i%20 == 0:
+    ...         print
+    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
+    21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
+    41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
+    61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
+    81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+
+    >>> import gc
+    >>> x = gc.collect()
+
+    """
+
+def old_test_add():
+    """test_add.py from old EC
+    
+    >>> class foo(Base):
+    ...     def __add__(self,other): print 'add called'
+
+    
+    >>> foo()+foo()
+    add called
+    """
+
+def proper_error_on_deleattr():
+    """
+    Florent Guillaume wrote:
+
+    ...
+
+    Excellent.
+    Will it also fix this particularity of ExtensionClass:
+    
+    
+    >>> class A(Base):
+    ...   def foo(self):
+    ...     self.gee
+    ...   def bar(self):
+    ...     del self.gee
+    
+    >>> a=A()
+    >>> a.foo()
+    Traceback (most recent call last):
+    ...
+    AttributeError: gee
+    
+    >>> a.bar()
+    Traceback (most recent call last):
+    ...
+    AttributeError: 'A' object has no attribute 'gee'
+    
+    I.e., the fact that KeyError is raised whereas a normal class would
+    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__
+    {}
+    """
+
+def cmpattrs(self, other, *attrs):
+    for attr in attrs:
+        if attr[:3] in ('_v_', '_p_'):
+            continue
+        c = cmp(getattr(self, attr, None), getattr(other, attr, None))
+        if c:
+            return c
+    return 0
+
+class Simple(Base):
+    def __init__(self, name, **kw):
+        self.__name__ = name
+        self.__dict__.update(kw)
+        self._v_favorite_color = 'blue'
+        self._p_foo = 'bar'
+
+    def __cmp__(self, other):
+        return cmpattrs(self, other, '__class__', *(self.__dict__.keys()))
+
+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', '_p_splat', '_v_eek'
+    def __init__(self, s1, s2):
+        self.s1, self.s2 = s1, s2
+        self._v_eek = 1
+        self._p_splat = 2
+
+class SubSlotted(Slotted):
+    __slots__ = 's3', 's4'
+    def __init__(self, s1, s2, s3):
+        Slotted.__init__(self, s1, s2)
+        self.s3 = s3
+
+        
+    def __cmp__(self, other):
+        return cmpattrs(self, other, '__class__', 's1', 's2', 's3', 's4')
+
+
+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)
+        self._v_favorite_color = 'blue'
+        self._p_foo = 'bar'
+        
+    def __cmp__(self, other):
+        return cmpattrs(self, other,
+                        '__class__', 's1', 's2', 's3', 's4',
+                        *(self.__dict__.keys()))
+
+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
+
+    """
+
+def test_pickling_w_slots_w_empty_dict():
+    """
+    >>> x = SubSubSlotted('x', 'y', 'z')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> print_dict(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__()
+    >>> print_dict(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
+
+    """
+    
+def test_setattr_on_extension_type():
+    """
+    >>> for name in 'x', '_x', 'x_', '__x_y__', '___x__', '__x___', '_x_':
+    ...     setattr(Base, name, 1)
+    ...     print getattr(Base, name)
+    ...     delattr(Base, name)
+    ...     print getattr(Base, name, 0)
+    1
+    0
+    1
+    0
+    1
+    0
+    1
+    0
+    1
+    0
+    1
+    0
+    1
+    0
+
+    >>> Base.__foo__ = 1
+    Traceback (most recent call last):
+    ...
+    TypeError: can't set attributes of built-in/extension type """ \
+        """'ExtensionClass.Base' if the attribute name begins """ \
+        """and ends with __ and contains only 4 _ characters
+
+    >>> Base.__foo__
+    Traceback (most recent call last):
+    ...
+    AttributeError: type object 'ExtensionClass.Base' """ \
+        """has no attribute '__foo__'
+
+    >>> del Base.__foo__
+    Traceback (most recent call last):
+    ...
+    TypeError: can't set attributes of built-in/extension type """ \
+        """'ExtensionClass.Base' if the attribute name begins """ \
+        """and ends with __ and contains only 4 _ characters
+
+    """
+
+def test_mro():
+    """ExtensionClass method-resolution order
+
+    The EC MRO is chosen to maximize backward compatibility and
+    provide a model that is easy to reason about.  The basic idea is:
+
+    I'll call this the "encapsulated base"  scheme.
+
+    Consider:
+
+      >>> class X(Base):
+      ...    pass
+      >>> class Y(Base):
+      ...    pass
+      >>> class Z(Base):
+      ...    pass
+
+      >>> class C(X, Y, Z):
+      ...    def foo(self):
+      ...       return 42
+
+    When we look up an attribute, we do the following:
+
+    - Look in C's dictionary first.
+
+    - Look up the attribute in X.  We don't care how we get the
+      attribute from X. If X is a new-style-class, we use the new
+      algorithm. If X is a classic class, we use left-to-right
+      depth-first. If X is an nsEC, use the "encapsulated base"
+      algorithm.
+
+      If we don't find the attribute in X, look in Y and then in Z,
+      using the same approach.
+
+      This algorithm will produce backward compatible results, providing
+      the equivalent of left-to-right depth-first for nsECs and classic
+      classes.
+
+    We'll actually do something less abstract.  We'll use a simple
+    algorthm to merge the __mro__ of the base classes, computing an
+    __mro__ for classic classes using the left-to-right depth-first
+    algorithm. We'll basically lay the mros end-to-end left-to-right
+    and remove repeats, keeping the first occurence of each class.
+
+    >>> [c.__name__ for c in C.__mro__]
+    ['C', 'X', 'Y', 'Z', 'Base', 'object']
+
+    For backward-compatability's sake, we actually depart from the
+    above description a bit. We always put Base and object last in the
+    mro, as shown in the example above. The primary reason for this is
+    that object provides a do-nothing __init__ method.  It is common
+    practice to mix a C-implemented base class that implements a few
+    methods with a Python class that implements those methods and
+    others. The idea is that the C implementation overrides selected
+    methods in C, so the C subclass is listed first. Unfortunately,
+    because all extension classes are required to subclass Base, and
+    thus, object, the C subclass brings along the __init__ object
+    from objects, which would hide any __init__ method provided by the
+    Python mix-in.
+
+    Base and object are special in that they are implied by their meta
+    classes.   For example, a new-style class always has object as an
+    ancestor, even if it isn't listed as a base:
+
+    >>> class O: 
+    ...     __metaclass__ = type
+    
+    >>> [c.__name__ for c in O.__bases__]
+    ['object']
+    >>> [c.__name__ for c in O.__mro__]
+    ['O', 'object']
+
+    Similarly, Base is always an ancestor of an extension class:
+
+    >>> class E: 
+    ...     __metaclass__ = ExtensionClass
+    
+    >>> [c.__name__ for c in E.__bases__]
+    ['Base']
+    >>> [c.__name__ for c in E.__mro__]
+    ['E', 'Base', 'object']
+    
+    Base and object are generally added soley to get a particular meta
+    class. They aren't used to provide application functionality and
+    really shouldn't be considered when reasoning about where
+    attributes come from.  They do provide some useful default
+    functionality and should be included at the end of the mro.
+
+    Here are more examples:
+
+    >>> from ExtensionClass import Base
+
+    >>> class NA(object):
+    ...  pass
+    >>> class NB(NA):
+    ...  pass
+    >>> class NC(NA):
+    ...  pass
+    >>> class ND(NB, NC):
+    ...  pass
+    >>> [c.__name__ for c in ND.__mro__]
+    ['ND', 'NB', 'NC', 'NA', 'object']
+
+    >>> class EA(Base):
+    ...  pass
+    >>> class EB(EA):
+    ...  pass
+    >>> class EC(EA):
+    ...  pass
+    >>> class ED(EB, EC):
+    ...  pass
+    >>> [c.__name__ for c in ED.__mro__]
+    ['ED', 'EB', 'EA', 'EC', 'Base', 'object']
+
+    >>> class EE(ED, ND):
+    ...  pass
+    >>> [c.__name__ for c in EE.__mro__]
+    ['EE', 'ED', 'EB', 'EA', 'EC', 'ND', 'NB', 'NC', 'NA', 'Base', 'object']
+
+    >>> class EF(ND, ED):
+    ...  pass
+    >>> [c.__name__ for c in EF.__mro__]
+    ['EF', 'ND', 'NB', 'NC', 'NA', 'ED', 'EB', 'EA', 'EC', 'Base', 'object']
+
+    >>> class CA:
+    ...  pass
+    >>> class CB(CA):
+    ...  pass
+    >>> class CC(CA):
+    ...  pass
+    >>> class CD(CB, CC):
+    ...  pass
+
+    >>> class ECD(Base, CD):
+    ...  pass
+    >>> [c.__name__ for c in ECD.__mro__]
+    ['ECD', 'CD', 'CB', 'CA', 'CC', 'Base', 'object']
+
+    >>> class CDE(CD, Base):
+    ...  pass
+    >>> [c.__name__ for c in CDE.__mro__]
+    ['CDE', 'CD', 'CB', 'CA', 'CC', 'Base', 'object']
+
+    >>> class CEND(CD, ED, ND):
+    ...  pass
+    >>> [c.__name__ for c in CEND.__mro__]
+    ['CEND', 'CD', 'CB', 'CA', 'CC', """ \
+       """'ED', 'EB', 'EA', 'EC', 'ND', 'NB', 'NC', 'NA', 'Base', 'object']
+    """
+
+def test_avoiding___init__decoy_w_inheritedAttribute():
+    """
+
+    >>> class Decoy(Base):
+    ...    pass
+
+    >>> class B(Base):
+    ...    def __init__(self, a, b):
+    ...       print '__init__', a, b
+
+    >>> class C(Decoy, B):
+    ...    def __init__(self):
+    ...       print 'C init'
+    ...       C.inheritedAttribute('__init__')(self, 1, 2)
+
+    >>> x = C()
+    C init
+    __init__ 1 2
+    
+    """
+
+def test_of_not_called_when_not_accessed_through_EC_instance():
+    """
+
+    >>> class Eek(Base):
+    ...     def __of__(self, parent):
+    ...         return self, parent
+
+    If I define an EC instance as an attr of an ordinary class:
+    
+    >>> class O(object):
+    ...     eek = Eek()
+    
+    >>> class C:
+    ...     eek = Eek()
+
+    I get the instance, without calling __of__, when I get it from
+    either tha class:
+
+    >>> O.eek is O.__dict__['eek']
+    True
+
+    >>> C.eek is C.__dict__['eek']
+    True
+
+    or an instance of the class:
+
+    >>> O().eek is O.__dict__['eek']
+    True
+
+    >>> C().eek is C.__dict__['eek']
+    True
+
+    If I define an EC instance as an attr of an extension class:
+    
+    >>> class E(Base):
+    ...     eek = Eek()
+    
+
+    I get the instance, without calling __of__, when I get it from
+    tha class:
+
+    >>> E.eek is E.__dict__['eek']
+    True
+
+    But __of__ is called if I go through the instance:
+
+    >>> e = E()
+    >>> e.eek == (E.__dict__['eek'], e)
+    True
+
+    """
+
+from doctest import DocTestSuite
+import unittest
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite('ExtensionClass'),
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()
+
+
+




More information about the Zope-Checkins mailing list