PyAPI: add missing options to bpy.data.library.load(..)

Support options which were available from the operator:
set_fake, recursive, reuse_local_id & clear_asset_data.
This commit is contained in:
Campbell Barton
2025-06-30 15:29:04 +10:00
parent b2ec149652
commit e9f675f40b
2 changed files with 132 additions and 26 deletions

View File

@@ -47,6 +47,28 @@
# include "bpy_rna.hh"
#endif
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
struct BoolFlagPair {
bool value;
uint32_t flag;
};
static uint32_t bool_flag_pair_as_flag(const BoolFlagPair *bool_flags, int bool_flags_num)
{
uint32_t flag = 0;
for (int i = 0; i < bool_flags_num; i++) {
BLI_assert(bool_flags[i].flag);
if (bool_flags[i].value) {
flag |= bool_flags[i].flag;
}
}
return flag;
}
/** \} */
struct BPy_Library {
PyObject_HEAD /* Required Python macro. */
/* Collection iterator specific parts. */
@@ -164,7 +186,11 @@ PyDoc_STRVAR(
"filepath, "
"link=False, "
"relative=False, "
"set_fake=False, "
"recursive=False, "
"reuse_local_id=False, "
"assets_only=False, "
"clear_asset_data=False, "
"create_liboverrides=False, "
"reuse_liboverrides=False, "
"create_liboverrides_runtime=False)\n"
@@ -178,8 +204,18 @@ PyDoc_STRVAR(
" :type link: bool\n"
" :arg relative: When True the path is stored relative to the open blend file.\n"
" :type relative: bool\n"
" :arg set_fake: If True, set fake user on appended IDs.\n"
" :type set_fake: bool\n"
" :arg recursive: If True, also make indirect dependencies of appended libraries local.\n"
" :type recursive: bool\n"
" :arg reuse_local_id: If True,"
"try to re-use previously appended matching ID on new append.\n"
" :type reuse_local_id: bool\n"
" :arg assets_only: If True, only list data-blocks marked as assets.\n"
" :type assets_only: bool\n"
" :arg clear_asset_data: If True, "
"clear the asset data on append (it is always kept for linked data).\n"
" :type clear_asset_data: bool\n"
" :arg create_liboverrides: If True and ``link`` is True, liboverrides will\n"
" be created for linked data.\n"
" :type create_liboverrides: bool\n"
@@ -195,15 +231,39 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
Main *bmain = static_cast<Main *>(self->ptr->data); /* Typically #G_MAIN */
BPy_Library *ret;
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
bool is_rel = false, is_link = false, use_assets_only = false;
bool create_liboverrides = false, reuse_liboverrides = false,
create_liboverrides_runtime = false;
/* #BPy_Library::flag
*
* - #BLO_LIBLINK_OBDATA_INSTANCE: The caller must manage instancing.
* - #BLO_LIBLINK_COLLECTION_INSTANCE: The caller must manage instancing.
*/
struct {
BoolFlagPair is_link = {false, FILE_LINK};
BoolFlagPair is_relative = {false, FILE_RELPATH};
BoolFlagPair set_fake = {false, BLO_LIBLINK_APPEND_SET_FAKEUSER};
BoolFlagPair recursive = {false, BLO_LIBLINK_APPEND_RECURSIVE};
BoolFlagPair reuse_local_id = {false, BLO_LIBLINK_APPEND_LOCAL_ID_REUSE};
BoolFlagPair assets_only = {false, FILE_ASSETS_ONLY};
BoolFlagPair clear_asset_data = {false, BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR};
} flag_vars;
bool create_liboverrides = false;
/* #BPy_Library::liboverride_flags */
struct {
BoolFlagPair reuse_liboverrides = {false, BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES};
BoolFlagPair create_liboverrides_runtime = {false, BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME};
} liboverride_flag_vars;
static const char *_keywords[] = {
"filepath",
"link",
"relative",
"set_fake",
"recursive",
"reuse_local_id",
"assets_only",
"clear_asset_data",
"create_liboverrides",
"reuse_liboverrides",
"create_liboverrides_runtime",
@@ -216,7 +276,11 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
"|$"
"O&" /* `link` */
"O&" /* `relative` */
"O&" /* `recursive` */
"O&" /* `set_fake` */
"O&" /* `reuse_local_id` */
"O&" /* `assets_only` */
"O&" /* `clear_asset_data` */
"O&" /* `create_liboverrides` */
"O&" /* `reuse_liboverrides` */
"O&" /* `create_liboverrides_runtime` */
@@ -230,17 +294,25 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
PyC_ParseUnicodeAsBytesAndSize,
&filepath_data,
PyC_ParseBool,
&is_link,
&flag_vars.is_link,
PyC_ParseBool,
&is_rel,
&flag_vars.is_relative,
PyC_ParseBool,
&use_assets_only,
&flag_vars.recursive,
PyC_ParseBool,
&flag_vars.set_fake,
PyC_ParseBool,
&flag_vars.reuse_local_id,
PyC_ParseBool,
&flag_vars.assets_only,
PyC_ParseBool,
&flag_vars.clear_asset_data,
PyC_ParseBool,
&create_liboverrides,
PyC_ParseBool,
&reuse_liboverrides,
&liboverride_flag_vars.reuse_liboverrides,
PyC_ParseBool,
&create_liboverrides_runtime))
&liboverride_flag_vars.create_liboverrides_runtime))
{
return nullptr;
}
@@ -271,19 +343,48 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
}
}
if (!is_link && create_liboverrides) {
PyErr_SetString(PyExc_ValueError, "`link` is False but `create_liboverrides` is True");
return nullptr;
if (flag_vars.is_link.value) {
/* Link. */
if (flag_vars.set_fake.value) {
PyErr_SetString(PyExc_ValueError, "`link` must be False if `set_fake` is True");
return nullptr;
}
if (flag_vars.recursive.value) {
PyErr_SetString(PyExc_ValueError, "`link` must be False if `recursive` is True");
return nullptr;
}
if (flag_vars.reuse_local_id.value) {
PyErr_SetString(PyExc_ValueError, "`link` must be False if `reuse_local_id` is True");
return nullptr;
}
if (flag_vars.clear_asset_data.value) {
PyErr_SetString(PyExc_ValueError, "`link` must be False if `clear_asset_data` is True");
return nullptr;
}
}
if (!create_liboverrides && reuse_liboverrides) {
PyErr_SetString(PyExc_ValueError,
"`create_liboverrides` is False but `reuse_liboverrides` is True");
return nullptr;
else {
/* Append. */
if (create_liboverrides) {
PyErr_SetString(PyExc_ValueError, "`link` is False but `create_liboverrides` is True");
return nullptr;
}
}
if (!create_liboverrides && create_liboverrides_runtime) {
PyErr_SetString(PyExc_ValueError,
"`create_liboverrides` is False but `create_liboverrides_runtime` is True");
return nullptr;
if (create_liboverrides) {
/* Library overrides. */
}
else {
/* Library overrides (disabled). */
if (liboverride_flag_vars.reuse_liboverrides.value) {
PyErr_SetString(PyExc_ValueError,
"`create_liboverrides` is False but `reuse_liboverrides` is True");
return nullptr;
}
if (liboverride_flag_vars.create_liboverrides_runtime.value) {
PyErr_SetString(PyExc_ValueError,
"`create_liboverrides` is False but `create_liboverrides_runtime` is True");
return nullptr;
}
}
ret = PyObject_New(BPy_Library, &bpy_lib_Type);
@@ -295,14 +396,16 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
ret->bmain_is_temp = (bmain != bmain_base);
ret->blo_handle = nullptr;
ret->flag = ((is_link ? FILE_LINK : 0) | (is_rel ? FILE_RELPATH : 0) |
(use_assets_only ? FILE_ASSETS_ONLY : 0));
ret->flag = bool_flag_pair_as_flag(reinterpret_cast<const BoolFlagPair *>(&flag_vars),
sizeof(flag_vars) / sizeof(BoolFlagPair));
ret->create_liboverrides = create_liboverrides;
ret->liboverride_flags = eBKELibLinkOverride(
create_liboverrides ?
((reuse_liboverrides ? BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES : 0) |
(create_liboverrides_runtime ? BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME : 0)) :
0);
ret->liboverride_flags = create_liboverrides ?
eBKELibLinkOverride(bool_flag_pair_as_flag(
reinterpret_cast<const BoolFlagPair *>(&liboverride_flag_vars),
sizeof(liboverride_flag_vars) / sizeof(BoolFlagPair))) :
eBKELibLinkOverride(0);
ret->dict = _PyDict_NewPresized(INDEX_ID_MAX);