PyAPI: use a new type for storing the deferred result of bpy.props
This is needed to support Python 3.10's Postponed annotation evaluation. It also simplifies type checking.
This commit is contained in:
parent
4cd808f912
commit
37e6a1995a
Notes:
blender-bot
2023-02-14 11:01:33 +01:00
Referenced by issue #85893, Geometry Nodes, modifiers after GN removes material Referenced by issue #85894, Grease pencil drawing on tablet
|
@ -193,6 +193,71 @@ static const EnumPropertyItem property_subtype_array_items[] = {
|
|||
"'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \
|
||||
" :type subtype: string\n"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Deferred Property Type
|
||||
*
|
||||
* Operators and classes use this so it can store the arguments given but defer
|
||||
* running it until the operator runs where these values are used to setup
|
||||
* the default arguments for that operator instance.
|
||||
* \{ */
|
||||
|
||||
static void bpy_prop_deferred_dealloc(BPy_PropDeferred *self)
|
||||
{
|
||||
if (self->kw) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->kw);
|
||||
}
|
||||
PyObject_GC_Del(self);
|
||||
}
|
||||
|
||||
static int bpy_prop_deferred_traverse(BPy_PropDeferred *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->kw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpy_prop_deferred_clear(BPy_PropDeferred *self)
|
||||
{
|
||||
Py_CLEAR(self->kw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *bpy_prop_deferred_repr(BPy_PropDeferred *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("<%.200s, %R, %R>", Py_TYPE(self)->tp_name, self->fn, self->kw);
|
||||
}
|
||||
|
||||
PyTypeObject bpy_prop_deferred_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
|
||||
.tp_name = "bpy_prop_deferred",
|
||||
.tp_basicsize = sizeof(BPy_PropDeferred),
|
||||
.tp_dealloc = (destructor)bpy_prop_deferred_dealloc,
|
||||
.tp_repr = (reprfunc)bpy_prop_deferred_repr,
|
||||
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
|
||||
.tp_traverse = (traverseproc)bpy_prop_deferred_traverse,
|
||||
.tp_clear = (inquiry)bpy_prop_deferred_clear,
|
||||
};
|
||||
|
||||
static PyObject *bpy_prop_deferred_data_CreatePyObject(PyObject *fn, PyObject *kw)
|
||||
{
|
||||
BPy_PropDeferred *self = PyObject_GC_New(BPy_PropDeferred, &bpy_prop_deferred_Type);
|
||||
self->fn = fn;
|
||||
if (kw == NULL) {
|
||||
kw = PyDict_New();
|
||||
}
|
||||
else {
|
||||
Py_INCREF(kw);
|
||||
}
|
||||
self->kw = kw;
|
||||
PyObject_GC_Track(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* PyObject's */
|
||||
static PyObject *pymeth_BoolProperty = NULL;
|
||||
static PyObject *pymeth_BoolVectorProperty = NULL;
|
||||
|
@ -248,27 +313,6 @@ static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_over
|
|||
RNA_def_property_override_flag(prop, flag_override);
|
||||
}
|
||||
|
||||
/* operators and classes use this so it can store the args given but defer
|
||||
* running it until the operator runs where these values are used to setup
|
||||
* the default args for that operator instance */
|
||||
static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw)
|
||||
{
|
||||
PyObject *ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(ret, 0, func);
|
||||
Py_INCREF(func);
|
||||
|
||||
if (kw == NULL) {
|
||||
kw = PyDict_New();
|
||||
}
|
||||
else {
|
||||
Py_INCREF(kw);
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(ret, 1, kw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callbacks */
|
||||
static void bpy_prop_update_cb(struct bContext *C,
|
||||
struct PointerRNA *ptr,
|
||||
|
@ -1997,7 +2041,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
|
|||
if (PyErr_Occurred()) { \
|
||||
return NULL; \
|
||||
} \
|
||||
return bpy_prop_deferred_return(pymeth_##_func, kw); \
|
||||
return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
|
@ -3668,5 +3712,9 @@ PyObject *BPY_rna_props(void)
|
|||
ASSIGN_STATIC(CollectionProperty);
|
||||
ASSIGN_STATIC(RemoveProperty);
|
||||
|
||||
if (PyType_Ready(&bpy_prop_deferred_Type) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,16 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
|
|||
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
|
||||
StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
/* This isn't GC tracked, it's a function from `bpy.props` so it's not going away. */
|
||||
void *fn;
|
||||
PyObject *kw;
|
||||
} BPy_PropDeferred;
|
||||
|
||||
extern PyTypeObject bpy_prop_deferred_Type;
|
||||
#define BPy_PropDeferred_CheckTypeExact(v) (Py_TYPE(v) == &bpy_prop_deferred_Type)
|
||||
|
||||
#define PYRNA_STACK_ARRAY RNA_STACK_ARRAY
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -4356,12 +4356,6 @@ static int pyrna_struct_pydict_contains(PyObject *self, PyObject *pyname)
|
|||
#endif
|
||||
|
||||
/* --------------- setattr------------------------------------------- */
|
||||
static bool pyrna_is_deferred_prop(const PyObject *value)
|
||||
{
|
||||
return PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value) == 2 &&
|
||||
PyCFunction_Check(PyTuple_GET_ITEM(value, 0)) &&
|
||||
PyDict_CheckExact(PyTuple_GET_ITEM(value, 1));
|
||||
}
|
||||
|
||||
#if 0
|
||||
static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr)
|
||||
|
@ -4373,12 +4367,12 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr
|
|||
* >>> bpy.types.Scene.foo
|
||||
* <bpy_struct, BoolProperty("foo")>
|
||||
* ...rather than returning the deferred class register tuple
|
||||
* as checked by pyrna_is_deferred_prop()
|
||||
* as checked by BPy_PropDeferred_CheckTypeExact()
|
||||
*
|
||||
* Disable for now,
|
||||
* this is faking internal behavior in a way that's too tricky to maintain well. */
|
||||
# if 0
|
||||
if ((ret == NULL) /* || pyrna_is_deferred_prop(ret) */ ) {
|
||||
if ((ret == NULL) /* || BPy_PropDeferred_CheckTypeExact(ret) */ ) {
|
||||
StructRNA *srna = srna_from_self(cls, "StructRNA.__getattr__");
|
||||
if (srna) {
|
||||
PropertyRNA *prop = RNA_struct_type_find_property(srna, PyUnicode_AsUTF8(attr));
|
||||
|
@ -4399,7 +4393,7 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr
|
|||
static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyObject *value)
|
||||
{
|
||||
StructRNA *srna = srna_from_self(cls, "StructRNA.__setattr__");
|
||||
const bool is_deferred_prop = (value && pyrna_is_deferred_prop(value));
|
||||
const bool is_deferred_prop = (value && BPy_PropDeferred_CheckTypeExact(value));
|
||||
const char *attr_str = PyUnicode_AsUTF8(attr);
|
||||
|
||||
if (srna && !pyrna_write_check() &&
|
||||
|
@ -7873,78 +7867,69 @@ StructRNA *srna_from_self(PyObject *self, const char *error_prefix)
|
|||
|
||||
static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item)
|
||||
{
|
||||
if (!BPy_PropDeferred_CheckTypeExact(item)) {
|
||||
/* No error, ignoring. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only care about results from C which
|
||||
* are for sure types, save some time with error */
|
||||
if (pyrna_is_deferred_prop(item)) {
|
||||
PyObject *py_func = ((BPy_PropDeferred *)item)->fn;
|
||||
PyObject *py_kw = ((BPy_PropDeferred *)item)->kw;
|
||||
PyObject *py_srna_cobject, *py_ret;
|
||||
|
||||
PyObject *py_func, *py_kw, *py_srna_cobject, *py_ret;
|
||||
PyObject *args_fake;
|
||||
|
||||
if (PyArg_ParseTuple(item, "OO!", &py_func, &PyDict_Type, &py_kw)) {
|
||||
PyObject *args_fake;
|
||||
if (*PyUnicode_AsUTF8(key) == '_') {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" registration error: "
|
||||
"%.200s could not register because the property starts with an '_'\n",
|
||||
RNA_struct_identifier(srna),
|
||||
PyUnicode_AsUTF8(key));
|
||||
return -1;
|
||||
}
|
||||
py_srna_cobject = PyCapsule_New(srna, NULL, NULL);
|
||||
|
||||
if (*PyUnicode_AsUTF8(key) == '_') {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" registration error: "
|
||||
"%.200s could not register because the property starts with an '_'\n",
|
||||
RNA_struct_identifier(srna),
|
||||
PyUnicode_AsUTF8(key));
|
||||
return -1;
|
||||
}
|
||||
py_srna_cobject = PyCapsule_New(srna, NULL, NULL);
|
||||
/* Not 100% nice :/, modifies the dict passed, should be ok. */
|
||||
PyDict_SetItem(py_kw, bpy_intern_str_attr, key);
|
||||
|
||||
/* Not 100% nice :/, modifies the dict passed, should be ok. */
|
||||
PyDict_SetItem(py_kw, bpy_intern_str_attr, key);
|
||||
args_fake = PyTuple_New(1);
|
||||
PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
|
||||
|
||||
args_fake = PyTuple_New(1);
|
||||
PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
|
||||
|
||||
PyObject *type = PyDict_GetItemString(py_kw, "type");
|
||||
StructRNA *type_srna = srna_from_self(type, "");
|
||||
if (type_srna) {
|
||||
if (!RNA_struct_idprops_datablock_allowed(srna) &&
|
||||
(*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
|
||||
*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) ==
|
||||
BPy_CollectionProperty) &&
|
||||
RNA_struct_idprops_contains_datablock(type_srna)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" doesn't support datablock properties\n",
|
||||
RNA_struct_identifier(srna));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
py_ret = PyObject_Call(py_func, args_fake, py_kw);
|
||||
|
||||
if (py_ret) {
|
||||
Py_DECREF(py_ret);
|
||||
Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
|
||||
}
|
||||
else {
|
||||
/* _must_ print before decreffing args_fake. */
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
|
||||
Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
|
||||
|
||||
// PyC_LineSpit();
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" registration error: "
|
||||
"%.200s could not register\n",
|
||||
RNA_struct_identifier(srna),
|
||||
PyUnicode_AsUTF8(key));
|
||||
return -1;
|
||||
}
|
||||
PyObject *type = PyDict_GetItemString(py_kw, "type");
|
||||
StructRNA *type_srna = srna_from_self(type, "");
|
||||
if (type_srna) {
|
||||
if (!RNA_struct_idprops_datablock_allowed(srna) &&
|
||||
(*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
|
||||
*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) &&
|
||||
RNA_struct_idprops_contains_datablock(type_srna)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" doesn't support datablock properties\n",
|
||||
RNA_struct_identifier(srna));
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* Since this is a class dict, ignore args that can't be passed. */
|
||||
}
|
||||
|
||||
/* For testing only. */
|
||||
#if 0
|
||||
PyC_ObSpit("Why doesn't this work??", item);
|
||||
PyErr_Print();
|
||||
#endif
|
||||
PyErr_Clear();
|
||||
}
|
||||
py_ret = PyObject_Call(py_func, args_fake, py_kw);
|
||||
|
||||
if (py_ret) {
|
||||
Py_DECREF(py_ret);
|
||||
Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
|
||||
}
|
||||
else {
|
||||
/* _must_ print before decreffing args_fake. */
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
|
||||
Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
|
||||
|
||||
// PyC_LineSpit();
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"bpy_struct \"%.200s\" registration error: "
|
||||
"%.200s could not register\n",
|
||||
RNA_struct_identifier(srna),
|
||||
PyUnicode_AsUTF8(key));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue