Commit 76ed044a authored by Jim Fulton's avatar Jim Fulton

Reimplemented computed attributes using new-style extension classes.

parent 565a1afd
/*****************************************************************************
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.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
****************************************************************************/
#include "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);
PyMem_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: _ComputedAttribute.c,v 1.2 2003/11/28 16:44:59 jim Exp $\n",
OBJECT(NULL),PYTHON_API_VERSION);
d = PyModule_GetDict(m);
PyExtensionClass_Export(d,"ComputedAttribute",ComputedAttributeType);
}
from _ComputedAttribute 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.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.
#
##############################################################################
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.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.
#
##############################################################################
"""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: tests.py,v 1.2 2003/11/28 16:44:59 jim Exp $
"""
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 doctest import DocTestSuite
from ExtensionClass import Base
from ComputedAttribute import ComputedAttribute
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