Blender V5.0
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#include "BLI_string_utils.hh"
20
21#include "BKE_context.hh"
22#include "BKE_library.hh"
23#include "BKE_main.hh"
24#include "BKE_report.hh"
25#include "BKE_text.h"
26
27#include "DNA_text_types.h"
28
29#include "BPY_extern_run.hh"
30
31#include "bpy_capi_utils.hh"
32#include "bpy_traceback.hh"
33
35
36/* -------------------------------------------------------------------- */
39
40static void python_script_error_jump_text(Text *text, const char *filepath)
41{
42 int lineno, lineno_end;
43 int offset, offset_end;
44 if (python_script_error_jump(filepath, &lineno, &offset, &lineno_end, &offset_end)) {
45 /* Start at the end so cursor motion that looses the selection,
46 * leaves the cursor from the most useful place.
47 * Also, the end can't always be set, so don't give it priority. */
48 txt_move_to(text, lineno_end - 1, offset_end - 1, false);
49 txt_move_to(text, lineno - 1, offset - 1, true);
50 }
51}
52
56static void bpy_text_filepath_get(char *filepath,
57 const size_t filepath_maxncpy,
58 const Main *bmain,
59 const Text *text)
60{
62 filepath, filepath_maxncpy, SEP, ID_BLEND_PATH(bmain, &text->id), text->id.name + 2);
63}
64
69 const char *filepath,
70 const int start,
71 PyObject *globals,
72 PyObject *locals,
73 const int closeit,
74 PyCompilerFlags *flags)
75{
76 /* Previously we used #PyRun_File to run directly the code on a FILE
77 * object, but as written in the Python/C API Ref Manual, chapter 2,
78 * 'FILE structs for different C libraries can be different and incompatible'.
79 * So now we load the script file data to a buffer on MS-Windows. */
80#ifdef _WIN32
81 bool use_file_handle_workaround = true;
82#else
83 bool use_file_handle_workaround = false;
84#endif
85
86 if (!use_file_handle_workaround) {
87 return PyRun_FileExFlags(fp, filepath, start, globals, locals, closeit, flags);
88 }
89
90 PyObject *py_result = nullptr;
91 size_t buf_len;
92 char *buf = static_cast<char *>(BLI_file_read_data_as_mem_from_handle(fp, false, 1, &buf_len));
93 if (closeit) {
94 fclose(fp);
95 }
96
97 if (UNLIKELY(buf == nullptr)) {
98 PyErr_Format(PyExc_IOError, "Python file \"%s\" could not read buffer", filepath);
99 }
100 else {
101 buf[buf_len] = '\0';
102 PyObject *filepath_py = PyC_UnicodeFromBytes(filepath);
103 PyObject *compiled = Py_CompileStringObject(buf, filepath_py, Py_file_input, flags, -1);
104 MEM_freeN(buf);
105 Py_DECREF(filepath_py);
106
107 if (compiled == nullptr) {
108 /* Based on Python's internal usage, an error must always be set. */
109 BLI_assert(PyErr_Occurred());
110 }
111 else {
112 py_result = PyEval_EvalCode(compiled, globals, locals);
113 Py_DECREF(compiled);
114 }
115 }
116 return py_result;
117}
118
128 bContext *C, const char *filepath, Text *text, ReportList *reports, const bool do_jump)
129{
130 BLI_assert(filepath || text);
131 if (filepath == nullptr && text == nullptr) {
132 return false;
133 }
134
135 PyGILState_STATE gilstate;
136 bpy_context_set(C, &gilstate);
137
138 Main *bmain_old = CTX_data_main(C);
139 PyObject *py_dict = nullptr, *py_result = nullptr;
140
141 char filepath_dummy[FILE_MAX];
143 const char *filepath_namespace = nullptr;
144
145 PyObject *main_mod = PyC_MainModule_Backup();
146
147 if (text) {
148 bpy_text_filepath_get(filepath_dummy, sizeof(filepath_dummy), bmain_old, text);
149 filepath_namespace = filepath_dummy;
150
151 if (text->compiled == nullptr) { /* if it wasn't already compiled, do it now */
152 PyObject *filepath_dummy_py = PyC_UnicodeFromBytes(filepath_dummy);
153 size_t buf_len_dummy;
154 char *buf = txt_to_buf(text, &buf_len_dummy);
155 text->compiled = Py_CompileStringObject(buf, filepath_dummy_py, Py_file_input, nullptr, -1);
156 MEM_freeN(buf);
157 Py_DECREF(filepath_dummy_py);
158 }
159
160 if (text->compiled) {
161 py_dict = PyC_DefaultNameSpace(filepath_dummy);
162 py_result = PyEval_EvalCode(static_cast<PyObject *>(text->compiled), py_dict, py_dict);
163 }
164 }
165 else {
166 FILE *fp = BLI_fopen(filepath, "rb");
167 filepath_namespace = filepath;
168
169 if (fp) {
170 /* Matches behavior of running Python with a directory argument.
171 * Without the `fstat`, the directory will execute & return None. */
172 BLI_stat_t st;
173 if (BLI_fstat(fileno(fp), &st) == 0 && S_ISDIR(st.st_mode)) {
174 PyErr_Format(PyExc_IsADirectoryError, "Python file \"%s\" is a directory", filepath);
175 BLI_assert(py_result == nullptr);
176 fclose(fp);
177 }
178 else {
179 /* Calls `fclose(fp)`, run the script with one fewer open files. */
180 const int closeit = 1;
181 py_dict = PyC_DefaultNameSpace(filepath);
183 fp, filepath, Py_file_input, py_dict, py_dict, closeit, nullptr);
184 }
185 }
186 else {
187 PyErr_Format(
188 PyExc_IOError, "Python file \"%s\" could not be opened: %s", filepath, strerror(errno));
189 BLI_assert(py_result == nullptr);
190 }
191 }
192
193 if (!py_result) {
194 if (reports) {
195 BPy_errors_to_report(reports);
196 }
197 if (text) {
198 if (do_jump) {
199 /* ensure text is valid before use, the script may have freed itself */
200 Main *bmain_new = CTX_data_main(C);
201 if ((bmain_old == bmain_new) && (BLI_findindex(&bmain_new->texts, text) != -1)) {
202 python_script_error_jump_text(text, filepath_namespace);
203 }
204 }
205 }
206 if (reports) {
207 PyErr_Clear();
208 }
209 else {
210 PyErr_Print();
211 }
212 }
213 else {
214 Py_DECREF(py_result);
215 }
216
217 PyC_MainModule_Restore(main_mod);
218
219 /* Flush `stdout` & `stderr` to ensure the script output is visible.
220 * Using `fflush(stdout)` does not solve it. */
222
223 bpy_context_clear(C, &gilstate);
224
225 return (py_result != nullptr);
226}
227
229
230/* -------------------------------------------------------------------- */
233
234bool BPY_run_filepath(bContext *C, const char *filepath, ReportList *reports)
235{
236 return python_script_exec(C, filepath, nullptr, reports, false);
237}
238
239bool BPY_run_text(bContext *C, Text *text, ReportList *reports, const bool do_jump)
240{
241 return python_script_exec(C, nullptr, text, reports, do_jump);
242}
243
249 const char *imports[],
250 const char *expr,
251 const int mode)
252{
253 BLI_assert(expr);
254 PyGILState_STATE gilstate;
255 PyObject *py_dict, *retval;
256 bool ok = true;
257
258 if (expr[0] == '\0') {
259 return ok;
260 }
261
262 bpy_context_set(C, &gilstate);
263
264 PyObject *main_mod = PyC_MainModule_Backup();
265
266 py_dict = PyC_DefaultNameSpace("<blender string>");
267
268 if (imports && !PyC_NameSpace_ImportArray(py_dict, imports)) {
269 Py_DECREF(py_dict);
270 retval = nullptr;
271 }
272 else {
273 retval = PyRun_String(expr, mode, py_dict, py_dict);
274 }
275
276 if (retval == nullptr) {
277 ok = false;
278 if (ReportList *wm_reports = C ? CTX_wm_reports(C) : nullptr) {
279 BPy_errors_to_report(wm_reports);
280 }
281 PyErr_Print();
282 }
283 else {
284 Py_DECREF(retval);
285 }
286
287 PyC_MainModule_Restore(main_mod);
288
289 bpy_context_clear(C, &gilstate);
290
291 return ok;
292}
293
294bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr)
295{
296 return bpy_run_string_impl(C, imports, expr, Py_eval_input);
297}
298
299bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
300{
301 return bpy_run_string_impl(C, imports, expr, Py_file_input);
302}
303
305
306/* -------------------------------------------------------------------- */
312
314{
315 BLI_assert(PyErr_Occurred());
316
317 if (err_info == nullptr) {
318 PyErr_Print();
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 bool ok = true;
364
365 if (expr[0] == '\0') {
366 *r_value = 0.0;
367 return ok;
368 }
369
370 PyGILState_STATE gilstate;
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 bool ok = true;
392
393 if (expr[0] == '\0') {
394 *r_value = nullptr;
395 return ok;
396 }
397
398 PyGILState_STATE gilstate;
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 bool ok = true;
427
428 if (expr[0] == '\0') {
429 *r_value = nullptr;
430 return ok;
431 }
432
433 PyGILState_STATE gilstate;
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 bool ok = true;
463
464 if (expr[0] == '\0') {
465 *r_value = 0;
466 return ok;
467 }
468
469 PyGILState_STATE gilstate;
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:325
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
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:1100
#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:454
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
#define BLI_string_join_by_sep_char(...)
#define UNLIKELY(x)
#define S_ISDIR(x)
#define ID_BLEND_PATH(_bmain, _id)
Definition DNA_ID.h:685
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
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: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)
Py_DECREF(oname)
const char * report_prefix
ReportList * reports
char name[258]
Definition DNA_ID.h:432
ListBase texts
Definition BKE_main.hh:294
void * compiled