static PyObject *
check2(PyObject *self, PyObject *other,
       char *opname, char *ropname, binaryfunc operation)
{
    PyObject *result = NULL;
    PyObject *object;

    if (Proxy_Check(self)) {
        object = Proxy_GET_OBJECT(self);
        result = operation(object, other);
    }
    else if (Proxy_Check(other)) {
        object = Proxy_GET_OBJECT(other);
        result = operation(self, object);
    }
    else {
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
    }
#if 0
    if (result != NULL)
        /* ??? create proxy for result? */
        ;
#endif
    return result;
}
static int
wrap_setitem(PyObject *self, PyObject *key, PyObject *value)
{
    if (value == NULL)
	return PyObject_DelItem(Proxy_GET_OBJECT(self), key);
    else
	return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value);
}
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);
}
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 *
wrapper_isProxy(PyObject *unused, PyObject *args)
{
  PyObject *obj, *result;
  PyTypeObject *proxytype=&ProxyType;

  if (! PyArg_ParseTuple(args, "O|O!:isProxy", 
                         &obj, &PyType_Type, &proxytype)
      )
    return NULL;

  while (obj && Proxy_Check(obj))
  {
    if (PyObject_TypeCheck(obj, proxytype))
      {
        result = Py_True;
	Py_INCREF(result);
        return result;
      }
    obj = Proxy_GET_OBJECT(obj);
  }
  result = Py_False;
  Py_INCREF(result);
  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_setattro(PyObject *self, PyObject *name, PyObject *value)
{
    PyObject *wrapped;
    PyObject *descriptor;
    const char *name_as_string;
    int res = -1;

#if PY_MAJOR_VERSION < 3 && defined(Py_USING_UNICODE)
    /* The Unicode to string conversion is done here because the
       existing tp_setattro slots expect a string object as name
       (except under Python 3) and we wouldn't want to break those. */

    if (PyUnicode_Check(name)) {
        name = PyUnicode_AsEncodedString(name, NULL, NULL);
        if (name == NULL)
            return -1;
    }
    else
#endif

    if (!IS_STRING(name)){
        PyErr_SetString(PyExc_TypeError, "attribute name must be string");
        return -1;
    }
    else
        Py_INCREF(name);

    descriptor = WrapperType_Lookup(self->ob_type, name);

    if (descriptor != NULL
#if PY_MAJOR_VERSION < 3 // This is always true in Python 3 (I think)
        && PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS)
#endif
        && descriptor->ob_type->tp_descr_set != NULL)
      {
        res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
        goto finally;
      }

    name_as_string = MAKE_STRING(name);

    wrapped = Proxy_GET_OBJECT(self);
    if (wrapped == NULL) {
        PyErr_Format(PyExc_RuntimeError,
            "object is NULL; requested to set attribute '%s'",
            name_as_string);
        goto finally;
    }
    res = PyObject_SetAttr(wrapped, name, value);

finally:
    Py_DECREF(name);
    return res;
}
static int
wrap_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value)
{
    PyObject *obj = Proxy_GET_OBJECT(self);
    if (PyList_Check(obj)) {
        return PyList_SetSlice(obj, i, j, value);
    }
    else {
        return PySequence_SetSlice(obj, i, j, value);
    }
}
static PyObject *
wrapper_getobject(PyObject *unused, PyObject *obj)
{
  if (Proxy_Check(obj)) 
    obj = Proxy_GET_OBJECT(obj);
  
  if (obj == NULL)
    obj = Py_None;

  Py_INCREF(obj);
  return obj;
}
static PyObject *
wrapper_removeAllProxies(PyObject *unused, PyObject *obj)
{
  while (obj && Proxy_Check(obj)) 
    obj = Proxy_GET_OBJECT(obj);
  
  if (obj == NULL)
    obj = Py_None;

  Py_INCREF(obj);
  return obj;
}
static PyObject *
check1(ProxyObject *self, char *opname, function1 operation)
{
    PyObject *result = NULL;

    result = operation(Proxy_GET_OBJECT(self));
#if 0
    if (result != NULL)
        /* ??? create proxy for result? */
        ;
#endif
    return result;
}
static PyObject *
wrapper_sameProxiedObjects(PyObject *unused, PyObject *args)
{
  PyObject *ob1, *ob2;

  if (! PyArg_ParseTuple(args, "OO:sameProxiedObjects", &ob1, &ob2))
    return NULL;

  while (ob1 && Proxy_Check(ob1)) 
    ob1 = Proxy_GET_OBJECT(ob1);

  while (ob2 && Proxy_Check(ob2)) 
    ob2 = Proxy_GET_OBJECT(ob2);

  if (ob1 == ob2)
    ob1 = Py_True;
  else
    ob1 = Py_False;

  Py_INCREF(ob1);
  return ob1;
}
static int
wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
{
    PyObject *wrapped;
    PyObject *descriptor;
    int res = -1;

#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 -1;
    }
    else
#endif
    if (!PyString_Check(name)){
        PyErr_SetString(PyExc_TypeError, "attribute name must be string");
        return -1;
    }
    else
        Py_INCREF(name);

    descriptor = WrapperType_Lookup(self->ob_type, name);
    if (descriptor != NULL) {
        if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) &&
            descriptor->ob_type->tp_descr_set != NULL) {
            res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
        } else {
            PyErr_Format(PyExc_TypeError,
                "Tried to set attribute '%s' on wrapper, but it is not"
                " a data descriptor", PyString_AS_STRING(name));
        }
        goto finally;
    }

    wrapped = Proxy_GET_OBJECT(self);
    if (wrapped == NULL) {
        PyErr_Format(PyExc_RuntimeError,
            "object is NULL; requested to set attribute '%s'",
            PyString_AS_STRING(name));
        goto finally;
    }
    res = PyObject_SetAttr(wrapped, name, value);

finally:
    Py_DECREF(name);
    return res;
}
static PyObject *
wrap_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
{
    PyObject *obj = Proxy_GET_OBJECT(self);
    if (PyList_Check(obj)) {
        return PyList_GetSlice(obj, start, end);
    }
    else if (PyTuple_Check(obj)) {
        return PyTuple_GetSlice(obj, start, end);
    }
    else {
        return PySequence_GetSlice(obj, start, end);
    }
}
static PyObject *
wrapper_setobject(PyObject *unused, PyObject *args)
{
  PyObject *proxy;
  PyObject *object;
  PyObject *result = NULL;
  if (PyArg_ParseTuple(args, "O!O:setProxiedObject",
                       &ProxyType, &proxy, &object)) {
    result = Proxy_GET_OBJECT(proxy);
    Py_INCREF(object);
    ((ProxyObject *) proxy)->proxy_object = object;
  }
  return result;
}
static PyObject *
wrap_pow(PyObject *self, PyObject *other, PyObject *modulus)
{
    PyObject *result = NULL;
    PyObject *object;

    if (Proxy_Check(self)) {
        object = Proxy_GET_OBJECT(self);
        result = PyNumber_Power(object, other, modulus);
    }
    else if (Proxy_Check(other)) {
        object = Proxy_GET_OBJECT(other);
        result = PyNumber_Power(self, object, modulus);
    }
    else if (modulus != NULL && Proxy_Check(modulus)) {
        object = Proxy_GET_OBJECT(modulus);
        result = PyNumber_Power(self, other, modulus);
    }
    else {
        Py_INCREF(Py_NotImplemented);
        return Py_NotImplemented;
    }
    return result;
}
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 PyObject *
wrapper_queryInnerProxy(PyObject *unused, PyObject *args)
{
  PyObject *obj, *result=Py_None;
  PyTypeObject *proxytype=&ProxyType;

  if (! PyArg_ParseTuple(args, "O|O!O:queryInnerProxy", 
                         &obj, &PyType_Type, &proxytype, &result)
      )
    return NULL;

  while (obj && Proxy_Check(obj))
  {
    if (PyObject_TypeCheck(obj, proxytype))
      result = obj;
    obj = Proxy_GET_OBJECT(obj);
  }

  Py_INCREF(result);
  return result;
}
static PyObject *
check2i(ProxyObject *self, PyObject *other,
	char *opname, binaryfunc operation)
{
	PyObject *result = NULL;
	PyObject *object = Proxy_GET_OBJECT(self);

        result = operation(object, other);
        if (result == object) {
            /* If the operation was really carried out inplace,
               don't create a new proxy, but use the old one. */
            Py_INCREF(self);
            Py_DECREF(object);
            result = (PyObject *)self;
        }
#if 0
        else if (result != NULL)
            /* ??? create proxy for result? */
            ;
#endif
	return result;
}
static int
wrap_coerce(PyObject **p_self, PyObject **p_other)
{
    PyObject *self = *p_self;
    PyObject *other = *p_other;
    PyObject *object;
    PyObject *left;
    PyObject *right;
    int r;

    assert(Proxy_Check(self));
    object = Proxy_GET_OBJECT(self);

    left = object;
    right = other;
    r = PyNumber_CoerceEx(&left, &right);
    if (r != 0)
        return r;
    /* Now left and right have been INCREF'ed.  Any new value that
       comes out is proxied; any unchanged value is left unchanged. */
    if (left == object) {
        /* Keep the old proxy */
        Py_INCREF(self);
        Py_DECREF(left);
        left = self;
    }
#if 0
    else {
        /* ??? create proxy for left? */
    }
    if (right != other) {
        /* ??? create proxy for right? */
    }
#endif
    *p_self = left;
    *p_other = right;
    return 0;
}
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_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_getitem(PyObject *wrapper, PyObject *v) {
    return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v);
}
static int
wrap_contains(PyObject *self, PyObject *value)
{
    return PySequence_Contains(Proxy_GET_OBJECT(self), value);
}
static int
wrap_ass_slice(PyObject *self, Py_ssize_t i, Py_ssize_t j, PyObject *value)
{
    return PySequence_SetSlice(Proxy_GET_OBJECT(self), i, j, value);
}
static PyObject *
wrap_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
{
    return PySequence_GetSlice(Proxy_GET_OBJECT(self), start, end);
}
static Py_ssize_t
wrap_length(PyObject *self)
{
    return PyObject_Length(Proxy_GET_OBJECT(self));
}
static int
wrap_nonzero(PyObject *self)
{
    return PyObject_IsTrue(Proxy_GET_OBJECT(self));
}