Blender V4.5
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
8
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_library.hh"
22#include "BKE_main.hh"
23#include "BKE_report.hh"
24#include "BKE_text.h"
25
26#include "DNA_text_types.h"
27
28#include "BPY_extern_run.hh"
29
30#include "bpy_capi_utils.hh"
31#include "bpy_traceback.hh"
32
34
35/* -------------------------------------------------------------------- */
38
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 BLI_assert(filepath || text);
134 if (filepath == nullptr && text == nullptr) {
135 return false;
136 }
137
138 PyGILState_STATE gilstate;
139 bpy_context_set(C, &gilstate);
140
141 Main *bmain_old = CTX_data_main(C);
142 PyObject *py_dict = nullptr, *py_result = nullptr;
143
144 char filepath_dummy[FILE_MAX];
146 const char *filepath_namespace = nullptr;
147
148 PyObject *main_mod = PyC_MainModule_Backup();
149
150 if (text) {
151 bpy_text_filepath_get(filepath_dummy, sizeof(filepath_dummy), bmain_old, text);
152 filepath_namespace = filepath_dummy;
153
154 if (text->compiled == nullptr) { /* if it wasn't already compiled, do it now */
155 PyObject *filepath_dummy_py = PyC_UnicodeFromBytes(filepath_dummy);
156 size_t buf_len_dummy;
157 char *buf = txt_to_buf(text, &buf_len_dummy);
158 text->compiled = Py_CompileStringObject(buf, filepath_dummy_py, Py_file_input, nullptr, -1);
159 MEM_freeN(buf);
160 Py_DECREF(filepath_dummy_py);
161 }
162
163 if (text->compiled) {
164 py_dict = PyC_DefaultNameSpace(filepath_dummy);
165 py_result = PyEval_EvalCode(static_cast<PyObject *>(text->compiled), py_dict, py_dict);
166 }
167 }
168 else {
169 FILE *fp = BLI_fopen(filepath, "rb");
170 filepath_namespace = filepath;
171
172 if (fp) {
173 /* Matches behavior of running Python with a directory argument.
174 * Without the `fstat`, the directory will execute & return None. */
175 BLI_stat_t st;
176 if (BLI_fstat(fileno(fp), &st) == 0 && S_ISDIR(st.st_mode)) {
177 PyErr_Format(PyExc_IsADirectoryError, "Python file \"%s\" is a directory", filepath);
178 BLI_assert(py_result == nullptr);
179 fclose(fp);
180 }
181 else {
182 /* Calls `fclose(fp)`, run the script with one fewer open files. */
183 const int closeit = 1;
184 py_dict = PyC_DefaultNameSpace(filepath);
186 fp, filepath, Py_file_input, py_dict, py_dict, closeit, nullptr);
187 }
188 }
189 else {
190 PyErr_Format(
191 PyExc_IOError, "Python file \"%s\" could not be opened: %s", filepath, strerror(errno));
192 BLI_assert(py_result == nullptr);
193 }
194 }
195
196 if (!py_result) {
197 if (reports) {
199 }
200 if (text) {
201 if (do_jump) {
202 /* ensure text is valid before use, the script may have freed itself */
203 Main *bmain_new = CTX_data_main(C);
204 if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
205 python_script_error_jump_text(text, filepath_namespace);
206 }
207 }
208 }
209 if (!reports) {
210 PyErr_Print();
211 }
212 PyErr_Clear();
213 }
214 else {
215 Py_DECREF(py_result);
216 }
217
218 PyC_MainModule_Restore(main_mod);
219
220 /* Flush `stdout` & `stderr` to ensure the script output is visible.
221 * Using `fflush(stdout)` does not solve it. */
223
224 bpy_context_clear(C, &gilstate);
225
226 return (py_result != nullptr);
227}
228
230
231/* -------------------------------------------------------------------- */
234
235bool BPY_run_filepath(bContext *C, const char *filepath, ReportList *reports)
236{
237 return python_script_exec(C, filepath, nullptr, reports, false);
238}
239
240bool BPY_run_text(bContext *C, Text *text, ReportList *reports, const bool do_jump)
241{
242 return python_script_exec(C, nullptr, text, reports, do_jump);
243}
244
250 const char *imports[],
251 const char *expr,
252 const int mode)
253{
254 BLI_assert(expr);
255 PyGILState_STATE gilstate;
256 PyObject *py_dict, *retval;
257 bool ok = true;
258
259 if (expr[0] == '\0') {
260 return ok;
261 }
262
263 bpy_context_set(C, &gilstate);
264
265 PyObject *main_mod = PyC_MainModule_Backup();
266
267 py_dict = PyC_DefaultNameSpace("<blender string>");
268
269 if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
270 Py_DECREF(py_dict);
271 retval = nullptr;
272 }
273 else {
274 retval = PyRun_String(expr, mode, py_dict, py_dict);
275 }
276
277 if (retval == nullptr) {
278 ok = false;
279 if (ReportList *wm_reports = C ? CTX_wm_reports(C) : nullptr) {
280 BPy_errors_to_report(wm_reports);
281 }
282 PyErr_Print();
283 PyErr_Clear();
284 }
285 else {
286 Py_DECREF(retval);
287 }
288
289 PyC_MainModule_Restore(main_mod);
290
291 bpy_context_clear(C, &gilstate);
292
293 return ok;
294}
295
296bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
297{
298 return bpy_run_string_impl(C, imports, expr, Py_eval_input);
299}
300
301bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
302{
303 return bpy_run_string_impl(C, imports, expr, Py_file_input);
304}
305
307
308/* -------------------------------------------------------------------- */
314
316{
317 BLI_assert(PyErr_Occurred());
318
319 if (err_info == nullptr) {
320 PyErr_Print();
321 PyErr_Clear();
322 return;
323 }
324
325 /* Signal to do nothing. */
326 if (!(err_info->reports || err_info->r_string)) {
327 PyErr_Clear();
328 return;
329 }
330
331 PyObject *py_err_str = err_info->use_single_line_error ? PyC_ExceptionBuffer_Simple() :
333 const char *err_str = PyUnicode_AsUTF8(py_err_str);
334 PyErr_Clear();
335
336 if (err_info->reports != nullptr) {
337 if (err_info->report_prefix) {
338 BKE_reportf(err_info->reports, RPT_ERROR, "%s: %s", err_info->report_prefix, err_str);
339 }
340 else {
341 BKE_report(err_info->reports, RPT_ERROR, err_str);
342 }
343 }
344
345 /* Print the reports if they were not printed already. */
346 if ((err_info->reports == nullptr) || !BKE_reports_print_test(err_info->reports, RPT_ERROR)) {
347 if (err_info->report_prefix) {
348 fprintf(stderr, "%s: ", err_info->report_prefix);
349 }
350 fprintf(stderr, "%s\n", err_str);
351 }
352
353 if (err_info->r_string != nullptr) {
354 *err_info->r_string = BLI_strdup(err_str);
355 }
356
357 Py_XDECREF(py_err_str);
358}
359
361 const char *imports[],
362 const char *expr,
363 BPy_RunErrInfo *err_info,
364 double *r_value)
365{
366 bool ok = true;
367
368 if (expr[0] == '\0') {
369 *r_value = 0.0;
370 return ok;
371 }
372
373 PyGILState_STATE gilstate;
374 bpy_context_set(C, &gilstate);
375
376 ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
377
378 if (ok == false) {
379 run_string_handle_error(err_info);
380 }
381
382 bpy_context_clear(C, &gilstate);
383
384 return ok;
385}
386
388 const char *imports[],
389 const char *expr,
390 BPy_RunErrInfo *err_info,
391 char **r_value,
392 size_t *r_value_len)
393{
394 bool ok = true;
395
396 if (expr[0] == '\0') {
397 *r_value = nullptr;
398 return ok;
399 }
400
401 PyGILState_STATE gilstate;
402 bpy_context_set(C, &gilstate);
403
404 ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_len);
405
406 if (ok == false) {
407 run_string_handle_error(err_info);
408 }
409
410 bpy_context_clear(C, &gilstate);
411
412 return ok;
413}
414
416 bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
417{
418 size_t value_dummy_len;
419 return BPY_run_string_as_string_and_len(C, imports, expr, err_info, r_value, &value_dummy_len);
420}
421
423 const char *imports[],
424 const char *expr,
425 BPy_RunErrInfo *err_info,
426 char **r_value,
427 size_t *r_value_len)
428{
429 bool ok = true;
430
431 if (expr[0] == '\0') {
432 *r_value = nullptr;
433 return ok;
434 }
435
436 PyGILState_STATE gilstate;
437 bpy_context_set(C, &gilstate);
438
440 imports, expr, "<expr as str or none>", r_value, r_value_len);
441
442 if (ok == false) {
443 run_string_handle_error(err_info);
444 }
445
446 bpy_context_clear(C, &gilstate);
447
448 return ok;
449}
450
452 bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value)
453{
454 size_t value_dummy_len;
456 C, imports, expr, err_info, r_value, &value_dummy_len);
457}
458
460 const char *imports[],
461 const char *expr,
462 BPy_RunErrInfo *err_info,
463 intptr_t *r_value)
464{
465 bool ok = true;
466
467 if (expr[0] == '\0') {
468 *r_value = 0;
469 return ok;
470 }
471
472 PyGILState_STATE gilstate;
473 bpy_context_set(C, &gilstate);
474
475 ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
476
477 if (ok == false) {
478 run_string_handle_error(err_info);
479 }
480
481 bpy_context_clear(C, &gilstate);
482
483 return ok;
484}
485
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:298
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:126
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:46
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:467
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define FILE_MAX
#define SEP
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
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)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
ReportList * reports
Definition WM_types.hh:1025
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)
#define ID_BLEND_PATH(_bmain, _id)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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)
void PyC_StdFilesFlush()
bool PyC_RunString_AsIntPtr(const char *imports[], const char *expr, const char *filename, intptr_t *r_value)
const char * report_prefix
ReportList * reports
char name[66]
Definition DNA_ID.h:415
ListBase texts
Definition BKE_main.hh:263
void * compiled