Blender V5.0
bpy_library_write.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include <Python.h>
13#include <cstddef>
14
15#include "BLI_path_utils.hh"
16#include "BLI_string.h"
17
18#include "BKE_blendfile.hh"
19#include "BKE_global.hh"
20#include "BKE_lib_id.hh"
21#include "BKE_main.hh"
22#include "BKE_report.hh"
23
24#include "BLO_writefile.hh"
25
26#include "RNA_types.hh"
27
28#include "bpy_capi_utils.hh"
29#include "bpy_library.hh" /* Declaration for #BPY_library_load_method_def */
30#include "bpy_rna.hh"
31
33#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
34
35using namespace blender::bke::blendfile;
36
38 /* Wrap. */
39 bpy_lib_write_doc,
40 ".. method:: write(filepath, datablocks, *, "
41 "path_remap=False, fake_user=False, compress=False)\n"
42 "\n"
43 " Write data-blocks into a blend file.\n"
44 "\n"
45 " .. note::\n"
46 "\n"
47 " Indirectly referenced data-blocks will be expanded and written too.\n"
48 "\n"
49 " :arg filepath: The path to write the blend-file.\n"
50 " :type filepath: str | bytes\n"
51 " :arg datablocks: set of data-blocks.\n"
52 " :type datablocks: set[:class:`bpy.types.ID`]\n"
53 " :arg path_remap: Optionally remap paths when writing the file:\n"
54 "\n"
55 " - ``NONE`` No path manipulation (default).\n"
56 " - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
57 " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
58 " - ``ABSOLUTE`` Make all paths absolute on writing.\n"
59 "\n"
60 " :type path_remap: str\n"
61 " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
62 " :type fake_user: bool\n"
63 " :arg compress: When True, write a compressed blend file.\n"
64 " :type compress: bool\n");
65static PyObject *bpy_lib_write(BPy_PropertyRNA *self, PyObject *args, PyObject *kw)
66{
67 /* args */
68 PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
69 char filepath_abs[FILE_MAX];
70 PyObject *datablocks = nullptr;
71
72 const PyC_StringEnumItems path_remap_items[] = {
75 {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
77 {0, nullptr},
78 };
79 PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
80
81 bool use_fake_user = false, use_compress = false;
82
83 static const char *_keywords[] = {
84 "filepath",
85 "datablocks",
86 "path_remap",
87 "fake_user",
88 "compress",
89 nullptr,
90 };
91 static _PyArg_Parser _parser = {
93 "O&" /* `filepath` */
94 "O!" /* `datablocks` */
95 "|$" /* Optional keyword only arguments. */
96 "O&" /* `path_remap` */
97 "O&" /* `fake_user` */
98 "O&" /* `compress` */
99 ":write",
100 _keywords,
101 nullptr,
102 };
103 if (!_PyArg_ParseTupleAndKeywordsFast(args,
104 kw,
105 &_parser,
107 &filepath_data,
108 &PySet_Type,
109 &datablocks,
111 &path_remap,
113 &use_fake_user,
115 &use_compress))
116 {
117 return nullptr;
118 }
119
120 Main *bmain_src = static_cast<Main *>(self->ptr->data); /* Typically #G_MAIN */
121 int write_flags = 0;
122
123 if (use_compress) {
124 write_flags |= G_FILE_COMPRESS;
125 }
126
127 STRNCPY(filepath_abs, filepath_data.value);
128 Py_XDECREF(filepath_data.value_coerce);
129
131
132 PartialWriteContext partial_write_ctx{*bmain_src};
133 const PartialWriteContext::IDAddOptions add_options{
137
138 if (PySet_GET_SIZE(datablocks) > 0) {
139 PyObject *it = PyObject_GetIter(datablocks);
140 PyObject *key;
141 while ((key = PyIter_Next(it))) {
142 /* Borrow from the set. */
143 Py_DECREF(key);
144 ID *id;
145 if (!pyrna_id_FromPyObject(key, &id)) {
146 PyErr_Format(PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(key)->tp_name);
147 break;
148 }
149 partial_write_ctx.id_add(id, add_options, nullptr);
150 }
151 Py_DECREF(it);
152 if (key) {
153 return nullptr;
154 }
155 }
156
157 BLI_assert(partial_write_ctx.is_valid());
158
159 /* write blend */
160 ReportList reports;
161
163 bool success = partial_write_ctx.write(
164 filepath_abs, write_flags, path_remap.value_found, reports);
165
166 PyObject *py_return_value;
167 if (success) {
169 py_return_value = Py_None;
170 Py_INCREF(py_return_value);
171 }
172 else {
173 if (BPy_reports_to_error(&reports, PyExc_IOError, false) == 0) {
174 PyErr_SetString(PyExc_IOError, "Unknown error writing library data");
175 }
176 py_return_value = nullptr;
177 }
178
179 BKE_reports_free(&reports);
180
181 return py_return_value;
182}
183
184#ifdef __GNUC__
185# ifdef __clang__
186# pragma clang diagnostic push
187# pragma clang diagnostic ignored "-Wcast-function-type"
188# else
189# pragma GCC diagnostic push
190# pragma GCC diagnostic ignored "-Wcast-function-type"
191# endif
192#endif
193
195 "write",
196 (PyCFunction)bpy_lib_write,
197 METH_VARARGS | METH_KEYWORDS,
198 bpy_lib_write_doc,
199};
200
201#ifdef __GNUC__
202# ifdef __clang__
203# pragma clang diagnostic pop
204# else
205# pragma GCC diagnostic pop
206# endif
207#endif
@ G_FILE_COMPRESS
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:892
@ RPT_PRINT_HANDLED_BY_OWNER
Definition BKE_report.hh:60
@ RPT_STORE
Definition BKE_report.hh:56
#define RPT_ERROR_ALL
Definition BKE_report.hh:51
void BKE_reports_free(ReportList *reports)
Definition report.cc:97
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:82
void BKE_reports_print(ReportList *reports, eReportType level)
Definition report.cc:356
#define BLI_assert(a)
Definition BLI_assert.h:46
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
external writefile.cc function prototypes.
@ BLO_WRITE_PATH_REMAP_NONE
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
@ BLO_WRITE_PATH_REMAP_RELATIVE
short BPy_reports_to_error(ReportList *reports, PyObject *exception, const bool clear)
PyObject * self
PyMethodDef BPY_library_write_method_def
PyDoc_STRVAR(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)
ID * id_add(const ID *id, IDAddOptions options, blender::FunctionRef< IDAddOperations(LibraryIDLinkCallbackData *cb_data, IDAddOptions options)> dependencies_filter_cb=nullptr)
bool write(const char *write_filepath, int write_flags, int remap_mode, ReportList &reports)
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
Definition bpy_rna.cc:8611
int PyC_ParseUnicodeAsBytesAndSize(PyObject *o, void *p)
int PyC_ParseStringEnum(PyObject *o, void *p)
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
Py_DECREF(oname)
#define PY_ARG_PARSER_HEAD_COMPAT()
Definition DNA_ID.h:414