[Zope-Checkins] CVS: Zope3/lib/python/Zope/ContextWrapper - proxy.c:1.1.2.1 proxy.h:1.1.2.1

Fred L. Drake, Jr. fdrake@acm.org
Thu, 25 Apr 2002 15:10:30 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/ContextWrapper
In directory cvs.zope.org:/tmp/cvs-serv322

Added Files:
      Tag: SecurityProxy-branch
	proxy.c proxy.h 
Log Message:
First cut at a proxy base class.

=== Added File Zope3/lib/python/Zope/ContextWrapper/proxy.c ===
#include "Python.h"
#include "modsupport.h"

#define PROXY_MODULE
#include "proxy.h"

staticforward PyTypeObject ProxyType;

#define Proxy_Check(wrapper) \
        (((wrapper)->ob_type == &ProxyType) \
         || (PyObject_TypeCheck(wrapper, &ProxyType)))

static PyObject *
empty_tuple = NULL;


/*
 *   Slot methods.
 */

static int
wrap_init(PyObject *self, PyObject *args, PyObject *kwds)
{
    int result = -1;
    PyObject *object;

    if (PyArg_UnpackTuple(args, "__init__", 1, 1, &object)) {
        ProxyObject *wrapper = (ProxyObject *)self;
        if (PyType_Type.tp_init(self, empty_tuple, NULL) < 0)
            goto finally;
        Py_INCREF(object);
        wrapper->proxy_object = object;
        result = 0;
    }
 finally:
    return result;
}

static int
wrap_traverse(PyObject *self, visitproc visit, void *arg)
{
    PyObject *ob = Proxy_GET_OBJECT(self);
    if (ob != NULL)
        return visit(ob, arg);
    else
        return 0;
}

static int
wrap_clear(PyObject *self)
{
    ProxyObject *proxy = (ProxyObject *)self;
    PyObject *temp = proxy->proxy_object;

    if (temp != NULL) {
        proxy->proxy_object = NULL;
        Py_DECREF(temp);
    }
    return 0;
}

static PyObject *
wrap_richcompare(PyObject* self, PyObject* other, int op)
{
    if (Proxy_Check(self)) {
        self = Proxy_GET_OBJECT(self);
    }
    else {
        other = Proxy_GET_OBJECT(other);
    }
    return PyObject_RichCompare(self, other, op);
}

static PyObject *
wrap_iter(PyObject *self)
{
    return PyObject_GetIter(Proxy_GET_OBJECT(self));
}

static void
wrap_dealloc(PyObject *self)
{
    (void) wrap_clear(self);
    self->ob_type->tp_free(self);
}

static PyObject *
wrap_getattro(PyObject *self, PyObject *name)
{
  return PyObject_GetAttr(Proxy_GET_OBJECT(self), name);
}

static int
wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
{
    if (Proxy_GET_OBJECT(self) != NULL)
        return PyObject_SetAttr(Proxy_GET_OBJECT(self), name, value);
    PyErr_Format(PyExc_RuntimeError,
                 "object is NULL; requested to set attribute '%s'",
                 PyString_AS_STRING(name));
    return -1;
}

static int
wrap_print(PyObject *wrapper, FILE *fp, int flags)
{
    return PyObject_Print(Proxy_GET_OBJECT(wrapper), fp, flags);
}

static PyObject *
wrap_str(PyObject *wrapper) {
    return PyObject_Str(Proxy_GET_OBJECT(wrapper));
}

static PyObject *
wrap_repr(PyObject *wrapper)
{
    return PyObject_Repr(Proxy_GET_OBJECT(wrapper));
}


static int
wrap_setattr(PyObject *wrapper, PyObject *name, PyObject *value)
{
    return PyObject_SetAttr(Proxy_GET_OBJECT(wrapper), name, value);
}

static int
wrap_compare(PyObject *wrapper, PyObject *v)
{
    return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v);
}

static long
wrap_hash(PyObject *self)
{
    return PyObject_Hash(Proxy_GET_OBJECT(self));
}

static PyObject *
wrap_call(PyObject *self, PyObject *args, PyObject *kw)
{
    if (kw)
        return PyEval_CallObjectWithKeywords(Proxy_GET_OBJECT(self),
					     args, kw);
    else
        return PyObject_CallObject(Proxy_GET_OBJECT(self), args);
}

/*
 *   Number methods
 */

static int
wrap_nonzero(PyObject *self)
{
    return PyObject_IsTrue(Proxy_GET_OBJECT(self));
}

/*
 *   Sequence methods
 */

static int
wrap_length(PyObject *self)
{
    return PyObject_Length(Proxy_GET_OBJECT(self));
}

static PyObject *
wrap_slice(PyObject *self, int start, int end)
{
    return PySequence_GetSlice(Proxy_GET_OBJECT(self), start, end);
}

static int
wrap_ass_slice(PyObject *self, int i, int j, PyObject *value)
{
    return PySequence_SetSlice(Proxy_GET_OBJECT(self), i, j, value);
}

static int
wrap_contains(PyObject *self, PyObject *value)
{
    return PySequence_Contains(Proxy_GET_OBJECT(self), value);
}

/*
 *   Mapping methods
 */

static PyObject *
wrap_getitem(PyObject *wrapper, PyObject *v) {
    return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v);
}

static int
wrap_setitem(PyObject *self, PyObject *key, PyObject *value)
{
    return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value);
}

/*
 *   Normal methods
 */

static char
reduce__doc__[] =
"__reduce__()\n"
"Raise an exception; this prevents proxies from being picklable by\n"
"default, even if the underlying object is picklable.";

static PyObject *
wrap_reduce(PyObject *self)
{
    PyObject *pickle_error = NULL;
    PyObject *pickle = PyImport_ImportModule("pickle");

    if (pickle == NULL)
        PyErr_Clear();
    else {
        pickle_error = PyObject_GetAttrString(pickle, "PicklingError");
        if (pickle_error == NULL)
            PyErr_Clear();
    }
    if (pickle_error == NULL) {
        pickle_error = PyExc_RuntimeError;
        Py_INCREF(pickle_error);
    }
    PyErr_SetString(pickle_error,
                    "proxy instances cannot be pickled");
    Py_DECREF(pickle_error);
    return NULL;
}

static PyNumberMethods
wrap_as_number = {
    0,					/* nb_add */
    0,					/* nb_subtract */
    0,					/* nb_multiply */
    0,					/* nb_divide */
    0,					/* nb_remainder */
    0,					/* nb_divmod */
    0,					/* nb_power */
    0,					/* nb_negative */
    0,					/* nb_positive */
    0,					/* nb_absolute */
    wrap_nonzero,			/* nb_nonzero */
};

static PySequenceMethods
wrap_as_sequence = {
    wrap_length,			/* sq_length */
    0,					/* sq_concat */
    0,					/* sq_repeat */
    0,					/* sq_item */
    wrap_slice,				/* sq_slice */
    0,					/* sq_ass_item */
    wrap_ass_slice,			/* sq_ass_slice */
    wrap_contains,			/* sq_contains */
};

static PyMappingMethods
wrap_as_mapping = {
    wrap_length,			/* mp_length */
    wrap_getitem,			/* mp_subscript */
    wrap_setitem,			/* mp_ass_subscript */
};

static PyMethodDef
wrap_methods[] = {
    {"__reduce__", (PyCFunction)wrap_reduce, METH_NOARGS, reduce__doc__},
    {NULL, NULL},
};

/*
 * Note that the numeric methods are not supported.  This is primarily
 * because of the way coercion-less operations are performed with
 * new-style numbers; since we can't tell which side of the operation
 * is 'self', we can't ensure we'd unwrap the right thing to perform
 * the actual operation.  We also can't afford to just unwrap both
 * sides the way weakrefs do, since we don't know what semantics will
 * be associated with the wrapper itself.
 */

statichere PyTypeObject
ProxyType = {
    PyObject_HEAD_INIT(NULL)
    0,
    "proxy.proxy",
    sizeof(ProxyObject),
    0,
    wrap_dealloc,			/* tp_dealloc */
    wrap_print,				/* tp_print */
    0,					/* tp_getattr */
    0,					/* tp_setattr */
    wrap_compare,			/* tp_compare */
    wrap_repr,				/* tp_repr */
    &wrap_as_number,			/* tp_as_number */
    &wrap_as_sequence,			/* tp_as_sequence */
    &wrap_as_mapping,			/* tp_as_mapping */
    wrap_hash,				/* tp_hash */
    wrap_call,					/* tp_call */
    wrap_str,				/* tp_str */
    wrap_getattro,			/* tp_getattro */
    wrap_setattro,			/* tp_setattro */
    0,					/* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC 
        | Py_TPFLAGS_BASETYPE, /* tp_flags */
    0,					/* tp_doc */
    wrap_traverse,			/* tp_traverse */
    wrap_clear,				/* tp_clear */
    wrap_richcompare,			/* tp_richcompare */
    0,					/* tp_weaklistoffset */
    wrap_iter,				/* tp_iter */
    0,					/* tp_iternext */
    wrap_methods,			/* tp_methods */
    0,					/* tp_members */
    0,					/* tp_getset */
    0,					/* tp_base */
    0,					/* tp_dict */
    0,					/* tp_descr_get */
    0,					/* tp_descr_set */
    0,					/* tp_dictoffset */
    wrap_init,				/* tp_init */
    PyType_GenericAlloc,		/* tp_alloc */
    PyType_GenericNew,			/* tp_new */
    _PyObject_GC_Del,			/* tp_free */
};


static PyObject *
create_proxy(PyObject *object)
{
    PyObject *result = NULL;
    PyObject *args;

    args = PyTuple_New(1);
    if (args != NULL) {
        Py_INCREF(object);
        PyTuple_SET_ITEM(args, 0, object);
        result = PyObject_CallObject((PyObject *)&ProxyType, args);
        Py_DECREF(args);
    }
    return result;
}

static int
api_check(PyObject *obj)
{
    return obj ? Proxy_Check(obj) : 0;
}

static PyObject *
api_create(PyObject *object)
{
    if (object == NULL) {
        PyErr_SetString(PyExc_ValueError,
                        "cannot create proxy around NULL");
        return NULL;
    }
    return create_proxy(object);
}

static PyObject *
api_getobject(PyObject *proxy)
{
    if (proxy == NULL) {
        PyErr_SetString(PyExc_RuntimeError,
			"cannot pass NULL to ProxyAPI.getobject()");
        return NULL;
    }
    if (Proxy_Check(proxy))
        return Proxy_GET_OBJECT(proxy);
    else {
        PyErr_Format(PyExc_TypeError, "expected proxy object, got %s",
		     proxy->ob_type->tp_name);
        return NULL;
    }
}

static ProxyInterface
wrapper_capi = {
    &ProxyType,
    api_check,
    api_create,
    api_getobject,
};

static PyObject *api_object = NULL;


static char
getobject__doc__[] =
"getobject(proxy) --> object\n"
"\n"
"Return the underlying object for proxy, or raise TypeError if it is\n"
"not a proxy.";

static PyObject *
wrapper_getobject(PyObject *unused, PyObject *obj)
{
    PyObject *result = NULL;

    if (Proxy_Check(obj)) {
        result = Proxy_GET_OBJECT(obj);
	Py_INCREF(result);
    }
    else
        PyErr_Format(PyExc_TypeError,
		     "expected proxy, got %s", obj->ob_type->tp_name);
    return result;
}

static PyMethodDef
module_functions[] = {
    {"getobject", wrapper_getobject, METH_O, getobject__doc__},
    {NULL}
};

static char
module___doc__[] =
"Association between an object, a context object, and a dictionary.\n\
\n\
The context object and dictionary give additional context information\n\
associated with a reference to the basic object.  The wrapper objects\n\
act as proxies for the original object.";


void
initproxy(void)
{
    PyObject *m = Py_InitModule3("proxy", module_functions, module___doc__);

    if (m == NULL)
        return;

    if (empty_tuple == NULL)
        empty_tuple = PyTuple_New(0);

    ProxyType.ob_type = &PyType_Type;
    if (PyType_Ready(&ProxyType) < 0)
        return;

    Py_INCREF(&ProxyType);
    PyModule_AddObject(m, "proxy", (PyObject *)&ProxyType);

    if (api_object == NULL) {
        api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL);
        if (api_object == NULL)
            return;
    }
    Py_INCREF(api_object);
    PyModule_AddObject(m, "_CAPI", api_object);
}


=== Added File Zope3/lib/python/Zope/ContextWrapper/proxy.h ===
#ifndef _proxy_H_
#define _proxy_H_ 1

typedef struct {
    PyObject_HEAD
    PyObject *proxy_object;
} ProxyObject;

#define Proxy_GET_OBJECT(ob)   (((ProxyObject *)(ob))->proxy_object)

typedef struct {
    PyTypeObject *proxytype;
    int (*check)(PyObject *obj);
    PyObject *(*create)(PyObject *obj);
    PyObject *(*getobject)(PyObject *proxy);
} ProxyInterface;


#ifndef PROXY_MODULE

/* These are only defined in the public interface, and are not
 * available within the module implementation.  There we use the
 * classic Python/C API only.
 */

static ProxyInterface *_proxy_api = NULL;

static int
Proxy_Import(void)
{
    if (_proxy_api == NULL) {
        PyObject *m = PyImport_ImportModule("Zope.ContextWrapper.proxy");
        if (m != NULL) {
            PyObject *tmp = PyObject_GetAttrString(m, "_CAPI");
            if (tmp != NULL) {
                if (PyCObject_Check(tmp))
                    _proxy_api = (ProxyInterface *)
                        PyCObject_AsVoidPtr(tmp);
                Py_DECREF(tmp);
            }
        }
    }
    return (_proxy_api == NULL) ? -1 : 0;
}

#define ProxyType               (_proxy_api->proxytype)
#define Proxy_Check(obj)        (_proxy_api->check((obj)))
#define Proxy_New(obj)          (_proxy_api->create((obj)))
#define Proxy_GetObject(proxy)  (_proxy_api->getobject((proxy)))

#endif /* PROXY_MODULE */

#endif /* _proxy_H_ */