Blender  V2.93
bpy_library_write.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
24 #include <Python.h>
25 #include <stddef.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "BLI_path_util.h"
30 #include "BLI_string.h"
31 #include "BLI_utildefines.h"
32 
33 #include "BKE_blendfile.h"
34 #include "BKE_global.h"
35 #include "BKE_main.h"
36 #include "BKE_report.h"
37 
38 #include "BLO_writefile.h"
39 
40 #include "RNA_types.h"
41 
42 #include "bpy_capi_utils.h"
43 #include "bpy_library.h"
44 #include "bpy_rna.h"
45 
46 #include "../generic/py_capi_utils.h"
47 
49  bpy_lib_write_doc,
50  ".. method:: write(filepath, datablocks, path_remap=False, fake_user=False, compress=False)\n"
51  "\n"
52  " Write data-blocks into a blend file.\n"
53  "\n"
54  " .. note::\n"
55  "\n"
56  " Indirectly referenced data-blocks will be expanded and written too.\n"
57  "\n"
58  " :arg filepath: The path to write the blend-file.\n"
59  " :type filepath: string\n"
60  " :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n"
61  " :type datablocks: set\n"
62  " :arg path_remap: Optionally remap paths when writing the file:\n"
63  "\n"
64  " - ``NONE`` No path manipulation (default).\n"
65  " - ``RELATIVE`` Remap paths that are already relative to the new location.\n"
66  " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n"
67  " - ``ABSOLUTE`` Make all paths absolute on writing.\n"
68  "\n"
69  " :type path_remap: string\n"
70  " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n"
71  " :type fake_user: bool\n"
72  " :arg compress: When True, write a compressed blend file.\n"
73  " :type compress: bool\n");
74 static PyObject *bpy_lib_write(BPy_PropertyRNA *self, PyObject *args, PyObject *kw)
75 {
76  /* args */
77  const char *filepath;
78  char filepath_abs[FILE_MAX];
79  PyObject *datablocks = NULL;
80 
81  const struct PyC_StringEnumItems path_remap_items[] = {
82  {BLO_WRITE_PATH_REMAP_NONE, "NONE"},
83  {BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"},
84  {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"},
85  {BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"},
86  {0, NULL},
87  };
88  struct PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE};
89 
90  bool use_fake_user = false, use_compress = false;
91 
92  static const char *_keywords[] = {
93  "filepath",
94  "datablocks",
95  /* optional */
96  "path_remap",
97  "fake_user",
98  "compress",
99  NULL,
100  };
101  static _PyArg_Parser _parser = {"sO!|$O&O&O&:write", _keywords, 0};
102  if (!_PyArg_ParseTupleAndKeywordsFast(args,
103  kw,
104  &_parser,
105  &filepath,
106  &PySet_Type,
107  &datablocks,
109  &path_remap,
111  &use_fake_user,
113  &use_compress)) {
114  return NULL;
115  }
116 
117  Main *bmain_src = self->ptr.data; /* Typically #G_MAIN */
118  int write_flags = 0;
119 
120  if (use_compress) {
121  write_flags |= G_FILE_COMPRESS;
122  }
123 
124  BLI_strncpy(filepath_abs, filepath, FILE_MAX);
126 
128 
129  /* array of ID's and backup any data we modify */
130  struct {
131  ID *id;
132  /* original values */
133  short id_flag;
134  short id_us;
135  } * id_store_array, *id_store;
136  int id_store_len = 0;
137 
138  PyObject *ret;
139 
140  /* collect all id data from the set and store in 'id_store_array' */
141  {
142  Py_ssize_t pos, hash;
143  PyObject *key;
144 
145  id_store_array = MEM_mallocN(sizeof(*id_store_array) * PySet_Size(datablocks), __func__);
146  id_store = id_store_array;
147 
148  pos = hash = 0;
149  while (_PySet_NextEntry(datablocks, &pos, &key, &hash)) {
150 
151  if (!pyrna_id_FromPyObject(key, &id_store->id)) {
152  PyErr_Format(PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(key)->tp_name);
153  ret = NULL;
154  goto finally;
155  }
156  else {
157  id_store->id_flag = id_store->id->flag;
158  id_store->id_us = id_store->id->us;
159 
160  if (use_fake_user) {
161  id_store->id->flag |= LIB_FAKEUSER;
162  }
163  id_store->id->us = 1;
164 
165  BKE_blendfile_write_partial_tag_ID(id_store->id, true);
166 
167  id_store_len += 1;
168  id_store++;
169  }
170  }
171  }
172 
173  /* write blend */
174  int retval = 0;
175  ReportList reports;
176 
177  BKE_reports_init(&reports, RPT_STORE);
179  bmain_src, filepath_abs, write_flags, path_remap.value_found, &reports);
180 
181  /* cleanup state */
183 
184  if (retval) {
185  BKE_reports_print(&reports, RPT_ERROR_ALL);
186  BKE_reports_clear(&reports);
187  ret = Py_None;
188  Py_INCREF(ret);
189  }
190  else {
191  if (BPy_reports_to_error(&reports, PyExc_IOError, true) == 0) {
192  PyErr_SetString(PyExc_IOError, "Unknown error writing library data");
193  }
194  ret = NULL;
195  }
196 
197 finally:
198 
199  /* clear all flags for ID's added to the store (may run on error too) */
200  id_store = id_store_array;
201 
202  for (int i = 0; i < id_store_len; id_store++, i++) {
203 
204  if (use_fake_user) {
205  if ((id_store->id_flag & LIB_FAKEUSER) == 0) {
206  id_store->id->flag &= ~LIB_FAKEUSER;
207  }
208  }
209 
210  id_store->id->us = id_store->id_us;
211 
212  BKE_blendfile_write_partial_tag_ID(id_store->id, false);
213  }
214 
215  MEM_freeN(id_store_array);
216 
217  return ret;
218 }
219 
221  "write",
222  (PyCFunction)bpy_lib_write,
223  METH_VARARGS | METH_KEYWORDS,
224  bpy_lib_write_doc,
225 };
void BKE_blendfile_write_partial_begin(struct Main *bmain_src)
Definition: blendfile.c:837
void BKE_blendfile_write_partial_end(struct Main *bmain_src)
Definition: blendfile.c:952
bool BKE_blendfile_write_partial(struct Main *bmain_src, const char *filepath, const int write_flags, const int remap_mode, struct ReportList *reports)
Definition: blendfile.c:871
void BKE_blendfile_write_partial_tag_ID(struct ID *id, bool set)
Definition: blendfile.c:842
@ G_FILE_COMPRESS
Definition: BKE_global.h:167
const char * BKE_main_blendfile_path_from_global(void)
Definition: main.c:439
void BKE_reports_print(ReportList *reports, ReportType level)
Definition: report.c:282
void BKE_reports_clear(ReportList *reports)
Definition: report.c:84
void BKE_reports_init(ReportList *reports, int flag)
Definition: report.c:66
#define FILE_MAX
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL()
Definition: path_util.c:1016
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
external writefile function prototypes.
@ BLO_WRITE_PATH_REMAP_NONE
Definition: BLO_writefile.h:43
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
Definition: BLO_writefile.h:47
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
Definition: BLO_writefile.h:49
@ BLO_WRITE_PATH_REMAP_RELATIVE
Definition: BLO_writefile.h:45
@ LIB_FAKEUSER
Definition: DNA_ID.h:477
#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:7622
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
#define hash
Definition: noise.c:169
int PyC_ParseStringEnum(PyObject *o, void *p)
int PyC_ParseBool(PyObject *o, void *p)
return ret
Definition: DNA_ID.h:273
Definition: BKE_main.h:116