Skip to content

Instantly share code, notes, and snippets.

@Vizonex
Created October 12, 2025 19:44
Show Gist options
  • Select an option

  • Save Vizonex/9419eeca3e9015b2615fff919a8eba74 to your computer and use it in GitHub Desktop.

Select an option

Save Vizonex/9419eeca3e9015b2615fff919a8eba74 to your computer and use it in GitHub Desktop.
propcache under_cached_property CPython Concept for moving propcache over to CPython instead of Cython for higher control over Refs and optimizing performance, this is currently incomplete hence me uploading it here.
/* This was a backup plan I had in mind if cimporting propcache from cython gets rejected
My main reason behind trying to force it in CPython or Cython is actually to come up with a new library
that deprecates property types (including cached properties) without needing to write
@deprecated(reason="blah blah blah")
@property
It is easier said as
@deprecated_property(reason="blah blah blah")
and it enhances readability
Adding propcache into this newly made deprecated properties library will help optimize
this section when it comes time for me to subclass
it in cython (if the cimporting idea gets rejected), otherwise I will have to play it dirty and copy and paste all of propcache's
code (it's minimal but rather costly) Licensing is not a concern since I will be using Appache 2.0 / MIT anyways...
*/
#include "Python.h"
typedef struct under_cached_property_object_s {
PyObject_HEAD
PyObject *wrapped;
PyObject *name;
} UnderCachedProperty;
static PyTypeObject UnderCachedProperty_Type;
#define UnderCachedProperty_CAST(op) ((UnderCachedProperty *)(op))
#define UnderCachedProperty_Check(v) PyObject_TypeCheck(v, &);
static int under_cached_property_init(
UnderCachedProperty* self,
PyObject* args,
PyObject* kw
){
PyObject* wrapped;
PyObject* name;
if (!PyArg_ParseTupleAndKeywords(args, kw, "O:__init__", wrapped)){
return -1;
}
Py_INCREF(wrapped);
self->wrapped = wrapped;
name = PyObject_GetAttrString(wrapped, "__name__");
if (name == NULL){
return -1;
}
Py_INCREF(name);
self->name = name;
return 0;
}
/* under_cached_property.__get__ */
static PyObject * under_cached_property_tp_descr_get(PyObject *op, PyObject *obj, PyObject *type){
PyObject* cache;
PyObject* val;
UnderCachedProperty* self ;
if (Py_IsNone(obj)){
Py_INCREF(op);
return op;
};
cache = PyObject_GetAttrString(obj, "_cache");
if (cache == NULL){
return NULL;
}
if (!PyDict_Check(cache)){
PyErr_Format(PyExc_TypeError, "Excepted _cache to be a dictionary like object not %R", cache);
return NULL;
}
Py_INCREF(cache);
self = UnderCachedProperty_CAST(op);
val = PyDict_GetItem(cache, self->name);
if (val == NULL){
val = PyObject_CallOneArg(self->wrapped, obj);
if (PyDict_SetItem(cache, self->name, val) < 0){
Py_DECREF(val);
Py_DECREF(cache);
return NULL;
};
Py_DECREF(val);
}
Py_DECREF(cache);
return val;
};
/* under_cached_property.__set__ */
static int under_cached_property_tp_descr_set(PyObject *self, PyObject *obj, PyObject *value){
PyErr_SetString(PyExc_AttributeError, "cached property is read-only");
return -1;
};
/* under_cached_property memebers */
static PyObject* under_cached_property_get__doc__(PyObject* self){
return PyObject_GetAttrString(UnderCachedProperty_CAST(self)->wrapped, "__doc__");
}
static PyObject* under_cached_property_get_wrapped(PyObject* op){
PyObject* wrapped = UnderCachedProperty_CAST(op)->wrapped;
Py_INCREF(wrapped);
return wrapped;
}
/* under_cached_property tp_traverse */
static int under_cached_property_tp_traverse(PyObject *op, visitproc visit, void *arg){
UnderCachedProperty* self = UnderCachedProperty_CAST(op);
Py_VISIT(self->wrapped);
Py_VISIT(self->name);
return 0;
};
static int under_cached_property_tp_clear(PyObject* op){
UnderCachedProperty* self = UnderCachedProperty_CAST(op);
Py_CLEAR(self->name);
Py_CLEAR(self->wrapped);
return 0;
}
static int under_cached_property_tp_dealloc(PyObject* op){
PyObject_GC_UnTrack(op);
UnderCachedProperty* self = UnderCachedProperty_CAST(op);
Py_CLEAR(self->name);
Py_CLEAR(self->wrapped);
Py_TYPE(op)->tp_free(op);
return 0;
}
static struct PyGetSetDef under_cached_property_getsetdef[] = {
{"__doc__", under_cached_property_get__doc__, 0, 0, 0},
{"wrapped", under_cached_property_get_wrapped, 0, 0, 0},
{0, 0, 0, 0, 0}
};
static PyType_Slot under_cached_property_tp_slots[] = {
{Py_tp_init, under_cached_property_init},
{Py_tp_descr_get, (void*)(under_cached_property_tp_descr_get)},
{Py_tp_descr_set, (void*)(under_cached_property_tp_descr_set)},
{0, NULL},
};
// static PyTypeObject UnderCachedProperty_Type = {
// PyVarObject_HEAD_INIT(0, 0)
// "propcache._helpers_c.""under_cached_property", /*tp_name*/
// sizeof(UnderCachedProperty), /*tp_basicsize*/
// 0, /*tp_itemsize*/
// under_cached_property_tp_dealloc, /*tp_dealloc*/
// #if PY_VERSION_HEX < 0x030800b4
// 0, /*tp_print*/
// #endif
// #if PY_VERSION_HEX >= 0x030800b4
// 0, /*tp_vectorcall_offset*/
// #endif
// }
// static PyType_Spec under_cached_property_spec = {
// .name = "_helpers_c.under_cached_property",
// .basicsize = sizeof(UnderCachedProperty),
// .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE),
// .slots = un,
// };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment