Blender V4.5
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, path_remap=False, fake_user=False, compress=False)\n"
41 "\n"
42 " Write data-blocks into a blend file.\n"
43 "\n"
44 " .. note::\n"
45 "\n"
46 " Indirectly referenced data-blocks will be expanded and written too.\n"
47 "\n"
48 " :arg filepath: The path to write the blend-file.\n"
49 " :type filepath: str | bytes\n"
50 " :arg datablocks: set of data-blocks.\n"
51 " :type datablocks: set[:class:`bpy.types.ID`]\n"
52 " :arg path_remap: Optionally remap paths when writing the file:\n"
53 "\n"
54 " - ``NONE`` No path manipulation (default).\n"
55 " - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
56 " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
57 " - ``ABSOLUTE`` Make all paths absolute on writing.\n"
58 "\n"
59 " :type path_remap: str\n"
60 " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
61 " :type fake_user: bool\n"
62 " :arg compress: When True, write a compressed blend file.\n"
63 " :type compress: bool\n");
64static PyObject *bpy_lib_write(BPy_PropertyRNA *self, PyObject *args, PyObject *kw)
65{
66 /* args */
67 PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
68 char filepath_abs[FILE_MAX];
69 PyObject *datablocks = nullptr;
70
71 const PyC_StringEnumItems path_remap_items[] = {
74 {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
76 {0, nullptr},
77 };
78 PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
79
80 bool use_fake_user = false, use_compress = false;
81
82 static const char *_keywords[] = {
83 "filepath",
84 "datablocks",
85 "path_remap",
86 "fake_user",
87 "compress",
88 nullptr,
89 };
90 static _PyArg_Parser _parser = {
92 "O&" /* `filepath` */
93 "O!" /* `datablocks` */
94 "|$" /* Optional keyword only arguments. */
95 "O&" /* `path_remap` */
96 "O&" /* `fake_user` */
97 "O&" /* `compress` */
98 ":write",
99 _keywords,
100 nullptr,
101 };
102 if (!_PyArg_ParseTupleAndKeywordsFast(args,
103 kw,
104 &_parser,
106 &filepath_data,
107 &PySet_Type,
108 &datablocks,
110 &path_remap,
112 &use_fake_user,
114 &use_compress))
115 {
116 return nullptr;
117 }
118
119 Main *bmain_src = static_cast<Main *>(self->ptr->data); /* Typically #G_MAIN */
120 int write_flags = 0;
121
122 if (use_compress) {
123 write_flags |= G_FILE_COMPRESS;
124 }
125
126 STRNCPY(filepath_abs, filepath_data.value);
127 Py_XDECREF(filepath_data.value_coerce);
128
130
131 PartialWriteContext partial_write_ctx{bmain_src->filepath};
132 const PartialWriteContext::IDAddOptions add_options{
136
137 if (PySet_GET_SIZE(datablocks) > 0) {
138 PyObject *it = PyObject_GetIter(datablocks);
139 PyObject *key;
140 while ((key = PyIter_Next(it))) {
141 /* Borrow from the set. */
142 Py_DECREF(key);
143 ID *id;
144 if (!pyrna_id_FromPyObject(key, &id)) {
145 PyErr_Format(PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(key)->tp_name);
146 break;
147 }
148 partial_write_ctx.id_add(id, add_options, nullptr);
149 }
150 Py_DECREF(it);
151 if (key) {
152 return nullptr;
153 }
154 }
155
156 BLI_assert(partial_write_ctx.is_valid());
157
158 /* write blend */
160
162 bool success = partial_write_ctx.write(
163 filepath_abs, write_flags, path_remap.value_found, reports);
164
165 PyObject *py_return_value;
166 if (success) {
168 py_return_value = Py_None;
169 Py_INCREF(py_return_value);
170 }
171 else {
172 if (BPy_reports_to_error(&reports, PyExc_IOError, false) == 0) {
173 PyErr_SetString(PyExc_IOError, "Unknown error writing library data");
174 }
175 py_return_value = nullptr;
176 }
177
179
180 return py_return_value;
181}
182
183#ifdef __GNUC__
184# ifdef __clang__
185# pragma clang diagnostic push
186# pragma clang diagnostic ignored "-Wcast-function-type"
187# else
188# pragma GCC diagnostic push
189# pragma GCC diagnostic ignored "-Wcast-function-type"
190# endif
191#endif
192
194 "write",
195 (PyCFunction)bpy_lib_write,
196 METH_VARARGS | METH_KEYWORDS,
197 bpy_lib_write_doc,
198};
199
200#ifdef __GNUC__
201# ifdef __clang__
202# pragma clang diagnostic pop
203# else
204# pragma GCC diagnostic pop
205# endif
206#endif
@ G_FILE_COMPRESS
const char * BKE_main_blendfile_path_from_global()
Definition main.cc:877
void BKE_reports_free(ReportList *reports)
Definition report.cc:70
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:55
void BKE_reports_print(ReportList *reports, eReportType level)
Definition report.cc:316
#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:688
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
#define RPT_ERROR_ALL
ReportList * reports
Definition WM_types.hh:1025
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:8499
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.
#define PY_ARG_PARSER_HEAD_COMPAT()
Definition DNA_ID.h:404
char filepath[1024]
Definition BKE_main.hh:155