Files
test2/source/blender/python/intern/bpy_library_write.cc
Brecht Van Lommel bb57ab3598 Fix #146448: Object copy/paste has wrong colors with working space
Write proper colorspace information into copy buffer blend files.

Pull Request: https://projects.blender.org/blender/blender/pulls/146450
2025-09-22 13:17:49 +02:00

208 lines
6.1 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup pythonintern
*
* Python API for writing a set of data-blocks into a file.
* Useful for writing out asset-libraries, defines: `bpy.data.libraries.write(...)`.
*/
#include <Python.h>
#include <cstddef>
#include "BLI_path_utils.hh"
#include "BLI_string.h"
#include "BKE_blendfile.hh"
#include "BKE_global.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_report.hh"
#include "BLO_writefile.hh"
#include "RNA_types.hh"
#include "bpy_capi_utils.hh"
#include "bpy_library.hh" /* Declaration for #BPY_library_load_method_def */
#include "bpy_rna.hh"
#include "../generic/py_capi_utils.hh"
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
using namespace blender::bke::blendfile;
PyDoc_STRVAR(
/* Wrap. */
bpy_lib_write_doc,
".. method:: write(filepath, datablocks, *, "
"path_remap=False, fake_user=False, compress=False)\n"
"\n"
" Write data-blocks into a blend file.\n"
"\n"
" .. note::\n"
"\n"
" Indirectly referenced data-blocks will be expanded and written too.\n"
"\n"
" :arg filepath: The path to write the blend-file.\n"
" :type filepath: str | bytes\n"
" :arg datablocks: set of data-blocks.\n"
" :type datablocks: set[:class:`bpy.types.ID`]\n"
" :arg path_remap: Optionally remap paths when writing the file:\n"
"\n"
" - ``NONE`` No path manipulation (default).\n"
" - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
" - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
" - ``ABSOLUTE`` Make all paths absolute on writing.\n"
"\n"
" :type path_remap: str\n"
" :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
" :type fake_user: bool\n"
" :arg compress: When True, write a compressed blend file.\n"
" :type compress: bool\n");
static PyObject *bpy_lib_write(BPy_PropertyRNA *self, PyObject *args, PyObject *kw)
{
/* args */
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
char filepath_abs[FILE_MAX];
PyObject *datablocks = nullptr;
const PyC_StringEnumItems path_remap_items[] = {
{BLO_WRITE_PATH_REMAP_NONE, "NONE"},
{BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"},
{BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
{BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"},
{0, nullptr},
};
PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
bool use_fake_user = false, use_compress = false;
static const char *_keywords[] = {
"filepath",
"datablocks",
"path_remap",
"fake_user",
"compress",
nullptr,
};
static _PyArg_Parser _parser = {
PY_ARG_PARSER_HEAD_COMPAT()
"O&" /* `filepath` */
"O!" /* `datablocks` */
"|$" /* Optional keyword only arguments. */
"O&" /* `path_remap` */
"O&" /* `fake_user` */
"O&" /* `compress` */
":write",
_keywords,
nullptr,
};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
PyC_ParseUnicodeAsBytesAndSize,
&filepath_data,
&PySet_Type,
&datablocks,
PyC_ParseStringEnum,
&path_remap,
PyC_ParseBool,
&use_fake_user,
PyC_ParseBool,
&use_compress))
{
return nullptr;
}
Main *bmain_src = static_cast<Main *>(self->ptr->data); /* Typically #G_MAIN */
int write_flags = 0;
if (use_compress) {
write_flags |= G_FILE_COMPRESS;
}
STRNCPY(filepath_abs, filepath_data.value);
Py_XDECREF(filepath_data.value_coerce);
BLI_path_abs(filepath_abs, BKE_main_blendfile_path_from_global());
PartialWriteContext partial_write_ctx{*bmain_src};
const PartialWriteContext::IDAddOptions add_options{
(PartialWriteContext::IDAddOperations::ADD_DEPENDENCIES |
PartialWriteContext::IDAddOperations(
use_fake_user ? PartialWriteContext::IDAddOperations::SET_FAKE_USER : 0))};
if (PySet_GET_SIZE(datablocks) > 0) {
PyObject *it = PyObject_GetIter(datablocks);
PyObject *key;
while ((key = PyIter_Next(it))) {
/* Borrow from the set. */
Py_DECREF(key);
ID *id;
if (!pyrna_id_FromPyObject(key, &id)) {
PyErr_Format(PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(key)->tp_name);
break;
}
partial_write_ctx.id_add(id, add_options, nullptr);
}
Py_DECREF(it);
if (key) {
return nullptr;
}
}
BLI_assert(partial_write_ctx.is_valid());
/* write blend */
ReportList reports;
BKE_reports_init(&reports, RPT_STORE);
bool success = partial_write_ctx.write(
filepath_abs, write_flags, path_remap.value_found, reports);
PyObject *py_return_value;
if (success) {
BKE_reports_print(&reports, RPT_ERROR_ALL);
py_return_value = Py_None;
Py_INCREF(py_return_value);
}
else {
if (BPy_reports_to_error(&reports, PyExc_IOError, false) == 0) {
PyErr_SetString(PyExc_IOError, "Unknown error writing library data");
}
py_return_value = nullptr;
}
BKE_reports_free(&reports);
return py_return_value;
}
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-function-type"
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif
#endif
PyMethodDef BPY_library_write_method_def = {
"write",
(PyCFunction)bpy_lib_write,
METH_VARARGS | METH_KEYWORDS,
bpy_lib_write_doc,
};
#ifdef __GNUC__
# ifdef __clang__
# pragma clang diagnostic pop
# else
# pragma GCC diagnostic pop
# endif
#endif