tornavis/source/blender/python/generic/py_capi_rna.cc

233 lines
5.8 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pygen
*
* Python/RNA utilities.
*
* RNA functions that aren't part of the `bpy_rna.cc` API.
*/
/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "py_capi_rna.h"
#include "BLI_bitmap.h"
#include "BLI_dynstr.h"
#include "RNA_access.hh"
#include "MEM_guardedalloc.h"
/* -------------------------------------------------------------------- */
/** \name Enum Utilities
* \{ */
char *pyrna_enum_repr(const EnumPropertyItem *item)
{
DynStr *dynstr = BLI_dynstr_new();
/* We can't compare with the first element in the array
* since it may be a category (without an identifier). */
for (bool is_first = true; item->identifier; item++) {
if (item->identifier[0]) {
BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
is_first = false;
}
}
char *cstring = BLI_dynstr_get_cstring(dynstr);
BLI_dynstr_free(dynstr);
return cstring;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Enum Conversion Utilities
* \{ */
int pyrna_enum_value_from_id(const EnumPropertyItem *item,
const char *identifier,
int *r_value,
const char *error_prefix)
{
if (RNA_enum_value_from_id(item, identifier, r_value) == 0) {
const char *enum_str = pyrna_enum_repr(item);
PyErr_Format(
PyExc_ValueError, "%s: '%.200s' not found in (%s)", error_prefix, identifier, enum_str);
MEM_freeN((void *)enum_str);
return -1;
}
return 0;
}
BLI_bitmap *pyrna_enum_bitmap_from_set(const EnumPropertyItem *items,
PyObject *value,
int type_size,
bool type_convert_sign,
int bitmap_size,
const char *error_prefix)
{
/* Set looping. */
Py_ssize_t pos = 0;
Py_ssize_t hash = 0;
PyObject *key;
BLI_bitmap *bitmap = BLI_BITMAP_NEW(bitmap_size, __func__);
while (_PySet_NextEntry(value, &pos, &key, &hash)) {
const char *param = PyUnicode_AsUTF8(key);
if (param == nullptr) {
PyErr_Format(PyExc_TypeError,
"%.200s expected a string, not %.200s",
error_prefix,
Py_TYPE(key)->tp_name);
goto error;
}
int ret;
if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) {
goto error;
}
int index = ret;
if (type_convert_sign) {
if (type_size == 2) {
union {
signed short as_signed;
ushort as_unsigned;
} ret_convert;
ret_convert.as_signed = (signed short)ret;
index = int(ret_convert.as_unsigned);
}
else if (type_size == 1) {
union {
signed char as_signed;
uchar as_unsigned;
} ret_convert;
ret_convert.as_signed = (signed char)ret;
index = int(ret_convert.as_unsigned);
}
else {
BLI_assert_unreachable();
}
}
BLI_assert(index < bitmap_size);
BLI_BITMAP_ENABLE(bitmap, index);
}
return bitmap;
error:
MEM_freeN(bitmap);
return nullptr;
}
int pyrna_enum_bitfield_from_set(const EnumPropertyItem *items,
PyObject *value,
int *r_value,
const char *error_prefix)
{
/* Set of enum items, concatenate all values with OR. */
int ret, flag = 0;
/* Set looping. */
Py_ssize_t pos = 0;
Py_ssize_t hash = 0;
PyObject *key;
*r_value = 0;
while (_PySet_NextEntry(value, &pos, &key, &hash)) {
const char *param = PyUnicode_AsUTF8(key);
if (param == nullptr) {
PyErr_Format(PyExc_TypeError,
"%.200s expected a string, not %.200s",
error_prefix,
Py_TYPE(key)->tp_name);
return -1;
}
if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) {
return -1;
}
flag |= ret;
}
*r_value = flag;
return 0;
}
PyObject *pyrna_enum_bitfield_as_set(const EnumPropertyItem *items, int value)
{
PyObject *ret = PySet_New(nullptr);
const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1];
if (RNA_enum_bitflag_identifiers(items, value, identifier)) {
PyObject *item;
int index;
for (index = 0; identifier[index]; index++) {
item = PyUnicode_FromString(identifier[index]);
PySet_Add(ret, item);
Py_DECREF(item);
}
}
return ret;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Argument Parsing Helpers
* \{ */
int pyrna_enum_value_parse_string(PyObject *o, void *p)
{
const char *identifier = PyUnicode_AsUTF8(o);
if (identifier == nullptr) {
PyErr_Format(PyExc_TypeError, "expected a string enum, not %.200s", Py_TYPE(o)->tp_name);
return 0;
}
BPy_EnumProperty_Parse *parse_data = static_cast<BPy_EnumProperty_Parse *>(p);
if (pyrna_enum_value_from_id(
parse_data->items, identifier, &parse_data->value, "enum identifier") == -1)
{
return 0;
}
parse_data->value_orig = o;
parse_data->is_set = true;
return 1;
}
int pyrna_enum_bitfield_parse_set(PyObject *o, void *p)
{
if (!PySet_Check(o)) {
PyErr_Format(PyExc_TypeError, "expected a set, not %.200s", Py_TYPE(o)->tp_name);
return 0;
}
BPy_EnumProperty_Parse *parse_data = static_cast<BPy_EnumProperty_Parse *>(p);
if (pyrna_enum_bitfield_from_set(
parse_data->items, o, &parse_data->value, "enum identifier set") == -1)
{
return 0;
}
parse_data->value_orig = o;
parse_data->is_set = true;
return 1;
}
/** \} */