[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