Blender V4.3
bpy_interface_run.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
9#include <cstdio>
10
11#include <Python.h>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_fileops.h"
16#include "BLI_listbase.h"
17#include "BLI_path_utils.hh"
18#include "BLI_string.h"
19
20#include "BKE_context.hh"
21#include "BKE_main.hh"
22#include "BKE_report.hh"
23#include "BKE_text.h"
24
25#include "DNA_text_types.h"
26
27#include "BPY_extern_run.hh"
28
29#include "bpy_capi_utils.hh"
30#include "bpy_intern_string.hh"
31#include "bpy_traceback.hh"
32
34
35/* -------------------------------------------------------------------- */
39static void python_script_error_jump_text(Text *text, const char *filepath)
40{
41 int lineno, lineno_end;
42 int offset, offset_end;
43 if (python_script_error_jump(filepath, &lineno, &offset, &lineno_end, &offset_end)) {
44 /* Start at the end so cursor motion that looses the selection,
45 * leaves the cursor from the most useful place.
46 * Also, the end can't always be set, so don't give it priority. */
47 txt_move_to(text, lineno_end - 1, offset_end - 1, false);
48 txt_move_to(text, lineno - 1, offset - 1, true);
49 }
50}
51
55static void bpy_text_filepath_get(char *filepath,
56 const size_t filepath_maxncpy,
57 const Main *bmain,
58 const Text *text)
59{
60 BLI_snprintf(filepath,
61 filepath_maxncpy,
62 "%s%c%s",
63 ID_BLEND_PATH(bmain, &text->id),
64 SEP,
65 text->id.name + 2);
66}
67
72 const char *filepath,
73 const int start,
74 PyObject *globals,
75 PyObject *locals,
76 const int closeit,
77 PyCompilerFlags *flags)
78{
79 /* Previously we used #PyRun_File to run directly the code on a FILE
80 * object, but as written in the Python/C API Ref Manual, chapter 2,
81 * 'FILE structs for different C libraries can be different and incompatible'.
82 * So now we load the script file data to a buffer on MS-Windows. */
83#ifdef _WIN32
84 bool use_file_handle_workaround = true;
85#else
86 bool use_file_handle_workaround = false;
87#endif
88
89 if (!use_file_handle_workaround) {
90 return PyRun_FileExFlags(fp, filepath, start, globals, locals, closeit, flags);
91 }
92
93 PyObject *py_result = nullptr;
94 size_t buf_len;
95 char *buf = static_cast<char *>(BLI_file_read_data_as_mem_from_handle(fp, false, 1, &buf_len));
96 if (closeit) {
97 fclose(fp);
98 }
99
100 if (UNLIKELY(buf == nullptr)) {
101 PyErr_Format(PyExc_IOError, "Python file \"%s\" could not read buffer", filepath);
102 }
103 else {
104 buf[buf_len] = '\0';
105 PyObject *filepath_py = PyC_UnicodeFromBytes(filepath);
106 PyObject *compiled = Py_CompileStringObject(buf, filepath_py, Py_file_input, flags, -1);
107 MEM_freeN(buf);
108 Py_DECREF(filepath_py);
109
110 if (compiled == nullptr) {
111 /* Based on Python's internal usage, an error must always be set. */
112 BLI_assert(PyErr_Occurred());
113 }
114 else {
115 py_result = PyEval_EvalCode(compiled, globals, locals);
116 Py_DECREF(compiled);
117 }
118 }
119 return py_result;
120}
121
131 bContext *C, const char *filepath, Text *text, ReportList *reports, const bool do_jump)
132{
133 Main *bmain_old = CTX_data_main(C);
134 PyObject *py_dict = nullptr, *py_result = nullptr;
135 PyGILState_STATE gilstate;
136
137 char filepath_dummy[FILE_MAX];
139 const char *filepath_namespace = nullptr;
140
141 BLI_assert(filepath || text);
142
143 if (filepath == nullptr && text == nullptr) {
144 return false;
145 }
146
147 bpy_context_set(C, &gilstate);
148
149 PyObject *main_mod = PyC_MainModule_Backup();
150
151 if (text) {
152 bpy_text_filepath_get(filepath_dummy, sizeof(filepath_dummy), bmain_old, text);
153 filepath_namespace = filepath_dummy;
154
155 if (text->compiled == nullptr) { /* if it wasn't already compiled, do it now */
156 PyObject *filepath_dummy_py = PyC_UnicodeFromBytes(filepath_dummy);
157 size_t buf_len_dummy;
158 char *buf = txt_to_buf(text, &buf_len_dummy);
159 text->compiled = Py_CompileStringObject(buf, filepath_dummy_py, Py_file_input, nullptr, -1);
160 MEM_freeN(buf);
161 Py_DECREF(filepath_dummy_py);
162 }
163
164 if (text->compiled) {
165 py_dict = PyC_DefaultNameSpace(filepath_dummy);
166 py_result = PyEval_EvalCode(static_cast<PyObject *>(text->compiled), py_dict, py_dict);
167 }
168 }
169 else {
170 FILE *fp = BLI_fopen(filepath, "rb");
171 filepath_namespace = filepath;
172
173 if (fp) {
174 /* Matches behavior of running Python with a directory argument.
175 * Without the `fstat`, the directory will execute & return None. */
176 BLI_stat_t st;
177 if (BLI_fstat(fileno(fp), &st) == 0 && S_ISDIR(st.st_mode)) {
178 PyErr_Format(PyExc_IsADirectoryError, "Python file \"%s\" is a directory", filepath);
179 BLI_assert(py_result == nullptr);
180 fclose(fp);
181 }
182 else {
183 /* Calls `fclose(fp)`, run the script with one fewer open files. */
184 const int closeit = 1;
185 py_dict = PyC_DefaultNameSpace(filepath);
187 fp, filepath, Py_file_input, py_dict, py_dict, closeit, nullptr);
188 }
189 }
190 else {
191 PyErr_Format(
192 PyExc_IOError, "Python file \"%s\" could not be opened: %s", filepath, strerror(errno));
193 BLI_assert(py_result == nullptr);
194 }
195 }
196
197 if (!py_result) {
198 if (reports) {
199 BPy_errors_to_report(reports);
200 }
201 if (text) {
202 if (do_jump) {
203 /* ensure text is valid before use, the script may have freed itself */
204 Main *bmain_new = CTX_data_main(C);
205 if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
206 python_script_error_jump_text(text, filepath_namespace);
207 }
208 }
209 }
210 if (!reports) {
211 PyErr_Print();
212 }
213 PyErr_Clear();
214 }
215 else {
216 Py_DECREF(py_result);
217 }
218
219 PyC_MainModule_Restore(main_mod);
220
221 bpy_context_clear(C, &gilstate);
222
223 return (py_result != nullptr);
224}
225
228/* -------------------------------------------------------------------- */
232bool BPY_run_filepath(bContext *C, const char *filepath, ReportList *reports)
233{
234 return python_script_exec(C, filepath, nullptr, reports, false);
235}
236
237bool BPY_run_text(bContext *C, Text *text, ReportList *reports, const bool do_jump)
238{
239 return python_script_exec(C, nullptr, text, reports, do_jump);
240}
241
247 const char *imports[],
248 const char *expr,
249 const int mode)
250{
251 BLI_assert(expr);
252 PyGILState_STATE gilstate;
253 PyObject *py_dict, *retval;
254 bool ok = true;
255
256 if (expr[0] == '\0') {
257 return ok;
258 }
259
260 bpy_context_set(C, &gilstate);
261
262 PyObject *main_mod = PyC_MainModule_Backup();
263
264 py_dict = PyC_DefaultNameSpace("<blender string>");
265
266 if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
267 Py_DECREF(py_dict);
268 retval = nullptr;
269 }
270 else {
271 retval = PyRun_String(expr, mode, py_dict, py_dict);
272 }
273
274 if (retval == nullptr) {
275 ok = false;
276 if (ReportList *wm_reports = C ? CTX_wm_reports(C) : nullptr) {
277 BPy_errors_to_report(wm_reports);
278 }
279 PyErr_Print();
280 PyErr_Clear();
281 }
282 else {
283 Py_DECREF(retval);
284 }
285
286 PyC_MainModule_Restore(main_mod);
287
288 bpy_context_clear(C, &gilstate);
289
290 return ok;
291}
292
293bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
294{
295 return bpy_run_string_impl(C, imports, expr, Py_eval_input);
296}
297
298bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
299{
300 return bpy_run_string_impl(C, imports, expr, Py_file_input);
301}
302
305/* -------------------------------------------------------------------- */
313{
314 BLI_assert(PyErr_Occurred());
315
316 if (err_info == nullptr) {
317 PyErr_Print();
318 PyErr_Clear();
319 return;
320 }
321
322 /* Signal to do nothing. */
323 if (!(err_info->reports || err_info->r_string)) {
324 PyErr_Clear();
325 return;
326 }
327
328 PyObject *py_err_str = err_info->use_single_line_error ? PyC_ExceptionBuffer_Simple() :
330 const char *err_str = PyUnicode_AsUTF8(py_err_str);
331 PyErr_Clear();
332
333 if (err_info->reports != nullptr) {
334 if (err_info->report_prefix) {
335 BKE_reportf(err_info->reports, RPT_ERROR, "%s: %s", err_info->report_prefix, err_str);
336 }
337 else {
338 BKE_report(err_info->reports, RPT_ERROR, err_str);
339 }
340 }
341
342 /* Print the reports if they were not printed already. */
343 if ((err_info->reports == nullptr) || !BKE_reports_print_test(err_info->reports, RPT_ERROR)) {
344 if (err_info->report_prefix) {
345 fprintf(stderr, "%s: ", err_info->report_prefix);
346 }
347 fprintf(stderr, "%s\n", err_str);
348 }
349
350 if (err_info->r_string != nullptr) {
351 *err_info->r_string = BLI_strdup(err_str);
352 }
353
354 Py_XDECREF(py_err_str);
355}
356
358 const char *imports[],
359 const char *expr,
360 BPy_RunErrInfo *err_info,
361 double *r_value)
362{
363 PyGILState_STATE gilstate;
364 bool ok = true;
365
366 if (expr[0] == '\0') {
367 *r_value = 0.0;
368 return ok;
369 }
370
371 bpy_context_set(C, &gilstate);
372
373 ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
374
375 if (ok == false) {
376 run_string_handle_error(err_info);
377 }
378
379 bpy_context_clear(C, &gilstate);
380
381 return ok;
382}
383
385 const char *imports[],
386 const char *expr,
387 BPy_RunErrInfo *err_info,
388 char **r_value,
389 size_t *r_value_len)
390{
391 PyGILState_STATE gilstate;
392 bool ok = true;
393
394 if (expr[0] == '\0') {
395 *r_value = nullptr;
396 return ok;
397 }
398
399 bpy_context_set(C, &gilstate);
400
401 ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_len);
402
403 if (ok == false) {
404 run_string_handle_error(err_info);
405 }
406
407 bpy_context_clear(C, &gilstate);
408
409 return ok;
410}
411
413 bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
414{
415 size_t value_dummy_len;
416 return BPY_run_string_as_string_and_len(C, imports, expr, err_info, r_value, &value_dummy_len);
417}
418
420 const char *imports[],
421 const char *expr,
422 BPy_RunErrInfo *err_info,
423 char **r_value,
424 size_t *r_value_len)
425{
426 PyGILState_STATE gilstate;
427 bool ok = true;
428
429 if (expr[0] == '\0') {
430 *r_value = nullptr;
431 return ok;
432 }
433
434 bpy_context_set(C, &gilstate);
435
437 imports, expr, "<expr as str or none>", r_value, r_value_len);
438
439 if (ok == false) {
440 run_string_handle_error(err_info);
441 }
442
443 bpy_context_clear(C, &gilstate);
444
445 return ok;
446}
447
449 bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
450{
451 size_t value_dummy_len;
453 C, imports, expr, err_info, r_value, &value_dummy_len);
454}
455
457 const char *imports[],
458 const char *expr,
459 BPy_RunErrInfo *err_info,
460 intptr_t *r_value)
461{
462 PyGILState_STATE gilstate;
463 bool ok = true;
464
465 if (expr[0] == '\0') {
466 *r_value = 0;
467 return ok;
468 }
469
470 bpy_context_set(C, &gilstate);
471
472 ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
473
474 if (ok == false) {
475 run_string_handle_error(err_info);
476 }
477
478 bpy_context_clear(C, &gilstate);
479
480 return ok;
481}
482
ReportList * CTX_wm_reports(const bContext *C)
Main * CTX_data_main(const bContext *C)
bool BKE_reports_print_test(const ReportList *reports, eReportType type)
Definition report.cc:297
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
char * txt_to_buf(struct Text *text, size_t *r_buf_strlen) ATTR_NONNULL(1
void txt_move_to(struct Text *text, unsigned int line, unsigned int ch, bool sel)
Definition text.cc:1099
#define BLI_assert(a)
Definition BLI_assert.h:50
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
struct stat BLI_stat_t
int BLI_fstat(int fd, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void * BLI_file_read_data_as_mem_from_handle(FILE *fp, bool read_size_exact, size_t pad_bytes, size_t *r_size)
Definition storage.cc:447
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define FILE_MAX
#define SEP
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define UNLIKELY(x)
#define S_ISDIR(x)
#define ID_BLEND_PATH(_bmain, _id)
Definition DNA_ID.h:647
Read Guarded memory(de)allocation.
bool BPy_errors_to_report(ReportList *reports)
void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate)
void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate)
bool BPY_run_string_as_string(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
bool BPY_run_filepath(bContext *C, const char *filepath, ReportList *reports)
bool BPY_run_string_as_number(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, double *r_value)
static void python_script_error_jump_text(Text *text, const char *filepath)
bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
bool BPY_run_string_as_string_and_len_or_none(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value, size_t *r_value_len)
static PyObject * python_compat_wrapper_PyRun_FileExFlags(FILE *fp, const char *filepath, const int start, PyObject *globals, PyObject *locals, const int closeit, PyCompilerFlags *flags)
bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
bool BPY_run_string_as_intptr(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, intptr_t *r_value)
static bool python_script_exec(bContext *C, const char *filepath, Text *text, ReportList *reports, const bool do_jump)
static void run_string_handle_error(BPy_RunErrInfo *err_info)
bool BPY_run_string_as_string_and_len(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value, size_t *r_value_len)
bool BPY_run_string_as_string_or_none(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
bool BPY_run_text(bContext *C, Text *text, ReportList *reports, const bool do_jump)
static void bpy_text_filepath_get(char *filepath, const size_t filepath_maxncpy, const Main *bmain, const Text *text)
static bool bpy_run_string_impl(bContext *C, const char *imports[], const char *expr, const int mode)
bool python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
PyObject * PyC_DefaultNameSpace(const char *filename)
bool PyC_RunString_AsStringAndSizeOrNone(const char *imports[], const char *expr, const char *filename, char **r_value, size_t *r_value_size)
PyObject * PyC_UnicodeFromBytes(const char *str)
PyObject * PyC_ExceptionBuffer()
bool PyC_RunString_AsNumber(const char *imports[], const char *expr, const char *filename, double *r_value)
PyObject * PyC_ExceptionBuffer_Simple()
PyObject * PyC_MainModule_Backup()
bool PyC_RunString_AsStringAndSize(const char *imports[], const char *expr, const char *filename, char **r_value, size_t *r_value_size)
bool PyC_NameSpace_ImportArray(PyObject *py_dict, const char *imports[])
void PyC_MainModule_Restore(PyObject *main_mod)
bool PyC_RunString_AsIntPtr(const char *imports[], const char *expr, const char *filename, intptr_t *r_value)
_W64 int intptr_t
Definition stdint.h:118
const char * report_prefix
ReportList * reports
ListBase texts
Definition BKE_main.hh:227