Commit 46a07c9d authored by Hanno Schlichting's avatar Hanno Schlichting

Moved out ExtensionClass, ComputedAttribute and MethodObject to its own package

parent 52347a7e
<extension _ComputedAttribute>
source _ComputedAttribute.c
</extension>
/*****************************************************************************
Copyright (c) 1996-2003 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (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
****************************************************************************/
#include "ExtensionClass/ExtensionClass.h"
#define UNLESS(E) if(!(E))
#define OBJECT(O) ((PyObject*)(O))
typedef struct {
PyObject_HEAD
PyObject *callable;
int level;
} CA;
static PyObject *
CA__init__(CA *self, PyObject *args)
{
PyObject *callable;
int level=0;
UNLESS(PyArg_ParseTuple(args,"O|i",&callable, &level)) return NULL;
if (level > 0)
{
callable=PyObject_CallFunction(OBJECT(self->ob_type), "Oi",
callable, level-1);
UNLESS (callable) return NULL;
self->level=level;
}
else
{
Py_INCREF(callable);
self->level=0;
}
self->callable=callable;
Py_INCREF(Py_None);
return Py_None;
}
static void
CA_dealloc(CA *self)
{
Py_DECREF(self->callable);
Py_DECREF(self->ob_type);
PyObject_DEL(self);
}
static PyObject *
CA_of(CA *self, PyObject *args)
{
if (self->level > 0)
{
Py_INCREF(self->callable);
return self->callable;
}
if (PyString_Check(self->callable))
{
/* Special case string as simple alias. */
PyObject *o;
UNLESS (PyArg_ParseTuple(args,"O", &o)) return NULL;
return PyObject_GetAttr(o, self->callable);
}
return PyObject_CallObject(self->callable, args);
}
static struct PyMethodDef CA_methods[] = {
{"__init__",(PyCFunction)CA__init__, METH_VARARGS, ""},
{"__of__", (PyCFunction)CA_of, METH_VARARGS, ""},
{NULL, NULL} /* sentinel */
};
static PyExtensionClass ComputedAttributeType = {
PyObject_HEAD_INIT(NULL) 0,
"ComputedAttribute", sizeof(CA),
0,
(destructor)CA_dealloc,
0,0,0,0,0, 0,0,0, 0,0,0,0,0, 0,0,
"ComputedAttribute(callable) -- Create a computed attribute",
METHOD_CHAIN(CA_methods),
(void*)(EXTENSIONCLASS_BINDABLE_FLAG)
};
static struct PyMethodDef methods[] = {
{NULL, NULL}
};
void
init_ComputedAttribute(void)
{
PyObject *m, *d;
UNLESS(ExtensionClassImported) return;
/* Create the module and add the functions */
m = Py_InitModule4("_ComputedAttribute", methods,
"Provide Computed Attributes\n\n"
"$Id$\n",
OBJECT(NULL),PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"ComputedAttribute",ComputedAttributeType);
}
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
from distutils.core import setup, Extension
setup(name="_ComputedAttribute", version="2.0",
ext_modules=[
Extension("_ComputedAttribute", ["_ComputedAttribute.c"],
include_dirs = ['.', '../ExtensionClass'],
depends = ['../ExtensionClass/ExtensionClass.h']),
])
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""Computed Attributes
Computed attributes work much like properties:
>>> import math
>>> class Point(Base):
... def __init__(self, x, y):
... self.x, self.y = x, y
... length = ComputedAttribute(lambda self: math.sqrt(self.x**2+self.y**2))
>>> p = Point(3, 4)
>>> "%.1f" % p.length
'5.0'
Except that you can also use computed attributes with instances:
>>> p.angle = ComputedAttribute(lambda self: math.atan(self.y*1.0/self.x))
>>> "%.2f" % p.angle
'0.93'
$Id$
"""
def test_wrapper_support():
"""Wrapper support
To support acquisition wrappers, computed attributes have a level.
The computation is only done when the level is zero. Retrieving a
computed attribute with a level > 0 returns a computed attribute
with a decremented level.
>>> class X(Base):
... pass
>>> x = X()
>>> x.n = 1
>>> x.n2 = ComputedAttribute(lambda self: self.n*2)
>>> x.n2
2
>>> x.n2.__class__.__name__
'int'
>>> x.n2 = ComputedAttribute(lambda self: self.n*2, 2)
>>> x.n2.__class__.__name__
'ComputedAttribute'
>>> x.n2 = x.n2
>>> x.n2.__class__.__name__
'ComputedAttribute'
>>> x.n2 = x.n2
>>> x.n2.__class__.__name__
'int'
"""
import unittest
from zope.testing.doctest import DocTestSuite
from ExtensionClass import Base
from ComputedAttribute import ComputedAttribute
def test_suite():
return unittest.TestSuite((DocTestSuite(),))
if __name__ == '__main__': unittest.main()
This diff is collapsed.
header ExtensionClass.h
<extension _ExtensionClass>
source _ExtensionClass.c
depends-on pickle/pickle.c
</extension>
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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 *
/*
Copyright (c) 2003 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (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.
*/
/*
Reusable pickle support code
This is "includeware", meant to be used through a C include
*/
/* It's a dang shame we can't inherit __get/setstate__ from object :( */
static PyObject *str__slotnames__, *copy_reg_slotnames, *__newobj__;
static PyObject *str__getnewargs__, *str__getstate__;
static int
pickle_setup(void)
{
PyObject *copy_reg;
int r = -1;
#define DEFINE_STRING(S) \
if(! (str ## S = PyString_FromString(# S))) return -1
DEFINE_STRING(__slotnames__);
DEFINE_STRING(__getnewargs__);
DEFINE_STRING(__getstate__);
#undef DEFINE_STRING
copy_reg = PyImport_ImportModule("copy_reg");
if (copy_reg == NULL)
return -1;
copy_reg_slotnames = PyObject_GetAttrString(copy_reg, "_slotnames");
if (copy_reg_slotnames == NULL)
goto end;
__newobj__ = PyObject_GetAttrString(copy_reg, "__newobj__");
if (__newobj__ == NULL)
goto end;
r = 0;
end:
Py_DECREF(copy_reg);
return r;
}
static PyObject *
pickle_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, (PyObject*)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 *
pickle_copy_dict(PyObject *state)
{
PyObject *copy, *key, *value;
char *ckey;
int pos = 0, nr;
copy = PyDict_New();
if (copy == NULL)
return NULL;
if (state == NULL)
return copy;
while ((nr = PyDict_Next(state, &pos, &key, &value)))
{
if (nr < 0)
goto err;
if (key && PyString_Check(key))
{
ckey = PyString_AS_STRING(key);
if (*ckey == '_' &&
(ckey[1] == 'v' || ckey[1] == 'p') &&
ckey[2] == '_')
/* skip volatile and persistent */
continue;
}
if (key != NULL && value != NULL &&
(PyObject_SetItem(copy, key, value) < 0)
)
goto err;
}
return copy;
err:
Py_DECREF(copy);
return NULL;
}
static char pickle___getstate__doc[] =
"Get the object serialization state\n"
"\n"
"If the object has no assigned slots and has no instance dictionary, then \n"
"None is returned.\n"
"\n"
"If the object has no assigned slots and has an instance dictionary, then \n"
"the a copy of the instance dictionary is returned. The copy has any items \n"
"with names starting with '_v_' or '_p_' ommitted.\n"
"\n"
"If the object has assigned slots, then a two-element tuple is returned. \n"
"The first element is either None or a copy of the instance dictionary, \n"
"as described above. The second element is a dictionary with items \n"
"for each of the assigned slots.\n"
;
static PyObject *
pickle___getstate__(PyObject *self)
{
PyObject *slotnames=NULL, *slots=NULL, *state=NULL;
PyObject **dictp;
int n=0;
slotnames = pickle_slotnames(self->ob_type);
if (slotnames == NULL)
return NULL;
dictp = _PyObject_GetDictPtr(self);
if (dictp)
state = pickle_copy_dict(*dictp);
else
{
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;
char *cname;
name = PyList_GET_ITEM(slotnames, i);
if (PyString_Check(name))
{
cname = PyString_AS_STRING(name);
if (*cname == '_' &&
(cname[1] == 'v' || cname[1] == 'p') &&
cname[2] == '_')
/* skip volatile and persistent */
continue;
}
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
pickle_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 char pickle___setstate__doc[] =
"Set the object serialization state\n"
"\n"
"The state should be in one of 3 forms:\n"
"\n"
"- None\n"
"\n"
" Ignored\n"
"\n"
"- A dictionary\n"
"\n"
" In this case, the object's instance dictionary will be cleared and \n"
" updated with the new state.\n"
"\n"
"- A two-tuple with a string as the first element. \n"
"\n"
" In this case, the method named by the string in the first element will be\n"
" called with the second element.\n"
"\n"
" This form supports migration of data formats.\n"
"\n"
"- A two-tuple with None or a Dictionary as the first element and\n"
" with a dictionary as the second element.\n"
"\n"
" If the first element is not None, then the object's instance dictionary \n"
" will be cleared and updated with the value.\n"
"\n"
" The items in the second element will be assigned as attributes.\n"
;
static PyObject *
pickle___setstate__(PyObject *self, PyObject *state)
{
PyObject *slots=NULL;
if (PyTuple_Check(state))
{
if (! PyArg_ParseTuple(state, "OO", &state, &slots))
return NULL;
}
if (state != Py_None)
{
PyObject **dict;
dict = _PyObject_GetDictPtr(self);
if (dict)
{
if (*dict == NULL)
{
*dict = PyDict_New();
if (*dict == NULL)
return NULL;
}
}
if (*dict != NULL)
{
PyDict_Clear(*dict);
if (PyDict_Update(*dict, state) < 0)
return NULL;
}
else if (pickle_setattrs_from_dict(self, state) < 0)
return NULL;
}
if (slots != NULL && pickle_setattrs_from_dict(self, slots) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static char pickle___getnewargs__doc[] =
"Get arguments to be passed to __new__\n"
;
static PyObject *
pickle___getnewargs__(PyObject *self)
{
return PyTuple_New(0);
}
static char pickle___reduce__doc[] =
"Reduce an object to contituent parts for serialization\n"
;
static PyObject *
pickle___reduce__(PyObject *self)
{
PyObject *args=NULL, *bargs=0, *state=NULL;
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, (PyObject*)(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;
}
#define PICKLE_GETSTATE_DEF \
{"__getstate__", (PyCFunction)pickle___getstate__, METH_NOARGS, \
pickle___getstate__doc},
#define PICKLE_SETSTATE_DEF \
{"__setstate__", (PyCFunction)pickle___setstate__, METH_O, \
pickle___setstate__doc},
#define PICKLE_GETNEWARGS_DEF \
{"__getnewargs__", (PyCFunction)pickle___getnewargs__, METH_NOARGS, \
pickle___getnewargs__doc},
#define PICKLE_REDUCE_DEF \
{"__reduce__", (PyCFunction)pickle___reduce__, METH_NOARGS, \
pickle___reduce__doc},
#define PICKLE_METHODS PICKLE_GETSTATE_DEF PICKLE_SETSTATE_DEF \
PICKLE_GETNEWARGS_DEF PICKLE_REDUCE_DEF
from distutils.core import setup, Extension
setup(name="ExtensionClass", version="2.0",
ext_modules=[
Extension("_ExtensionClass", ["_ExtensionClass.c"],
depends = ["ExtensionClass.h", "pickle/pickle.c"],
),
])
This diff is collapsed.
<extension _MethodObject>
source _MethodObject.c
</extension>
/*****************************************************************************
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.1 (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
****************************************************************************/
#include "ExtensionClass/ExtensionClass.h"
static PyObject *
of(PyObject *self, PyObject *args)
{
PyObject *inst;
if(PyArg_Parse(args,"O",&inst)) return PyECMethod_New(self,inst);
else return NULL;
}
struct PyMethodDef Method_methods[] = {
{"__of__",(PyCFunction)of,0,""},
{NULL, NULL} /* sentinel */
};
static struct PyMethodDef methods[] = {{NULL, NULL}};
void
init_MethodObject(void)
{
PyObject *m, *d;
PURE_MIXIN_CLASS(Method,
"Base class for objects that want to be treated as methods\n"
"\n"
"The method class provides a method, __of__, that\n"
"binds an object to an instance. If a method is a subobject\n"
"of an extension-class instance, the the method will be bound\n"
"to the instance and when the resulting object is called, it\n"
"will call the method and pass the instance in addition to\n"
"other arguments. It is the responsibility of Method objects\n"
"to implement (or inherit) a __call__ method.\n",
Method_methods);
/* Create the module and add the functions */
m = Py_InitModule4("_MethodObject", methods,
"Method-object mix-in class module\n\n"
"$Id$\n",
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"Method",MethodType);
}
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
from distutils.core import setup, Extension
setup(name="_MethodObject", version="2.0",
ext_modules=[
Extension("_MethodObject", ["_MethodObject.c"],
include_dirs = ['.', '../ExtensionClass'],
depends = ['../ExtensionClass/ExtensionClass.h']),
])
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""XXX short summary goes here.
$Id$
"""
import unittest
from zope.testing.doctest import DocTestSuite
def test_xxx():
"""
>>> from ExtensionClass import Base
>>> from MethodObject import Method
>>> class foo(Method):
... def __call__(self, ob, *args, **kw):
... print 'called', ob, args, kw
>>> class bar(Base):
... def __repr__(self):
... return "bar()"
... hi = foo()
>>> x = bar()
>>> hi = x.hi
>>> hi(1,2,3,name='spam')
called bar() (1, 2, 3) {'name': 'spam'}
"""
def test_suite():
return unittest.TestSuite((
DocTestSuite(),
))
if __name__ == '__main__': unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment