Blender  V3.3
bpy_library_write.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
10 #include <Python.h>
11 #include <stddef.h>
12 
13 #include "MEM_guardedalloc.h"
14 
15 #include "BLI_path_util.h"
16 #include "BLI_string.h"
17 #include "BLI_utildefines.h"
18 
19 #include "BKE_blendfile.h"
20 #include "BKE_global.h"
21 #include "BKE_main.h"
22 #include "BKE_report.h"
23 
24 #include "BLO_writefile.h"
25 
26 #include "RNA_types.h"
27 
28 #include "bpy_capi_utils.h"
29 #include "bpy_library.h"
30 #include "bpy_rna.h"
31 
32 #include "../generic/py_capi_utils.h"
33 
35  bpy_lib_write_doc,
36  ".. method:: write(filepath, datablocks, path_remap=False, fake_user=False, compress=False)\n"
37  "\n"
38  " Write data-blocks into a blend file.\n"
39  "\n"
40  " .. note::\n"
41  "\n"
42  " Indirectly referenced data-blocks will be expanded and written too.\n"
43  "\n"
44  " :arg filepath: The path to write the blend-file.\n"
45  " :type filepath: string\n"
46  " :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n"
47  " :type datablocks: set\n"
48  " :arg path_remap: Optionally remap paths when writing the file:\n"
49  "\n"
50  " - ``NONE`` No path manipulation (default).\n"
51  " - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
52  " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
53  " - ``ABSOLUTE`` Make all paths absolute on writing.\n"
54  "\n"
55  " :type path_remap: string\n"
56  " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
57  " :type fake_user: bool\n"
58  " :arg compress: When True, write a compressed blend file.\n"
59  " :type compress: bool\n");
60 static PyObject *bpy_lib_write(BPy_PropertyRNA *self, PyObject *args, PyObject *kw)
61 {
62  /* args */
63  const char *filepath;
64  char filepath_abs[FILE_MAX];
65  PyObject *datablocks = NULL;
66 
67  const struct PyC_StringEnumItems path_remap_items[] = {
68  {BLO_WRITE_PATH_REMAP_NONE, "NONE"},
69  {BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"},
70  {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
71  {BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"},
72  {0, NULL},
73  };
74  struct PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
75 
76  bool use_fake_user = false, use_compress = false;
77 
78  static const char *_keywords[] = {
79  "filepath",
80  "datablocks",
81  "path_remap",
82  "fake_user",
83  "compress",
84  NULL,
85  };
86  static _PyArg_Parser _parser = {
87  "s" /* `filepath` */
88  "O!" /* `datablocks` */
89  "|$" /* Optional keyword only arguments. */
90  "O&" /* `path_remap` */
91  "O&" /* `fake_user` */
92  "O&" /* `compress` */
93  ":write",
94  _keywords,
95  0,
96  };
97  if (!_PyArg_ParseTupleAndKeywordsFast(args,
98  kw,
99  &_parser,
100  &filepath,
101  &PySet_Type,
102  &datablocks,
104  &path_remap,
106  &use_fake_user,
108  &use_compress)) {
109  return NULL;
110  }
111 
112  Main *bmain_src = self->ptr.data; /* Typically #G_MAIN */
113  int write_flags = 0;
114 
115  if (use_compress) {
116  write_flags |= G_FILE_COMPRESS;
117  }
118 
119  BLI_strncpy(filepath_abs, filepath, FILE_MAX);
121 
123 
124  /* array of ID's and backup any data we modify */
125  struct {
126  ID *id;
127  /* original values */
128  short id_flag;
129  short id_us;
130  } * id_store_array, *id_store;
131  int id_store_len = 0;
132 
133  PyObject *ret;
134 
135  /* collect all id data from the set and store in 'id_store_array' */
136  {
137  Py_ssize_t pos, hash;
138  PyObject *key;
139 
140  id_store_array = MEM_mallocN(sizeof(*id_store_array) * PySet_Size(datablocks), __func__);
141  id_store = id_store_array;
142 
143  pos = hash = 0;
144  while (_PySet_NextEntry(datablocks, &pos, &key, &hash)) {
145 
146  if (!pyrna_id_FromPyObject(key, &id_store->id)) {
147  PyErr_Format(PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(key)->tp_name);
148  ret = NULL;
149  goto finally;
150  }
151  else {
152  id_store->id_flag = id_store->id->flag;
153  id_store->id_us = id_store->id->us;
154 
155  if (use_fake_user) {
156  id_store->id->flag |= LIB_FAKEUSER;
157  }
158  id_store->id->us = 1;
159 
160  BKE_blendfile_write_partial_tag_ID(id_store->id, true);
161 
162  id_store_len += 1;
163  id_store++;
164  }
165  }
166  }
167 
168  /* write blend */
169  int retval = 0;
170  ReportList reports;
171 
172  BKE_reports_init(&reports, RPT_STORE);
174  bmain_src, filepath_abs, write_flags, path_remap.value_found, &reports);
175 
176  /* cleanup state */
178 
179  if (retval) {
180  BKE_reports_print(&reports, RPT_ERROR_ALL);
181  BKE_reports_clear(&reports);
182  ret = Py_None;
183  Py_INCREF(ret);
184  }
185  else {
186  if (BPy_reports_to_error(&reports, PyExc_IOError, true) == 0) {
187  PyErr_SetString(PyExc_IOError, "Unknown error writing library data");
188  }
189  ret = NULL;
190  }
191 
192 finally:
193 
194  /* clear all flags for ID's added to the store (may run on error too) */
195  id_store = id_store_array;
196 
197  for (int i = 0; i < id_store_len; id_store++, i++) {
198 
199  if (use_fake_user) {
200  if ((id_store->id_flag & LIB_FAKEUSER) == 0) {
201  id_store->id->flag &= ~LIB_FAKEUSER;
202  }
203  }
204 
205  id_store->id->us = id_store->id_us;
206 
207  BKE_blendfile_write_partial_tag_ID(id_store->id, false);
208  }
209 
210  MEM_freeN(id_store_array);
211 
212  return ret;
213 }
214 
216  "write",
217  (PyCFunction)bpy_lib_write,
218  METH_VARARGS | METH_KEYWORDS,
219  bpy_lib_write_doc,
220 };
void BKE_blendfile_write_partial_begin(struct Main *bmain_src)
Definition: blendfile.c:872
void BKE_blendfile_write_partial_end(struct Main *bmain_src)
Definition: blendfile.c:984
bool BKE_blendfile_write_partial(struct Main *bmain_src, const char *filepath, int write_flags, int remap_mode, struct ReportList *reports)
Definition: blendfile.c:902
void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set)
Definition: blendfile.c:877
@ G_FILE_COMPRESS
Definition: BKE_global.h:210
const char * BKE_main_blendfile_path_from_global(void)
Definition: main.c:562
void BKE_reports_clear(ReportList *reports)
Definition: report.c:63
void BKE_reports_init(ReportList *reports, int flag)
Definition: report.c:50
void BKE_reports_print(ReportList *reports, eReportType level)
Definition: report.c:267
#define FILE_MAX
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL()
Definition: path_util.c:897
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
external writefile.c function prototypes.
@ BLO_WRITE_PATH_REMAP_NONE
Definition: BLO_writefile.h:31
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
Definition: BLO_writefile.h:35
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
Definition: BLO_writefile.h:37
@ BLO_WRITE_PATH_REMAP_RELATIVE
Definition: BLO_writefile.h:33
@ LIB_FAKEUSER
Definition: DNA_ID.h:630
#define RPT_ERROR_ALL
Read Guarded memory(de)allocation.
short BPy_reports_to_error(ReportList *reports, PyObject *exception, const bool clear)
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: string\n" " :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n" " :type datablocks: set\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: string\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)
PyMethodDef BPY_library_write_method_def
uint pos
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
Definition: bpy_rna.c:7657
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define hash
Definition: noise.c:153
int PyC_ParseStringEnum(PyObject *o, void *p)
int PyC_ParseBool(PyObject *o, void *p)
return ret
Definition: DNA_ID.h:368
Definition: BKE_main.h:121