[Bf-blender-cvs] [f8cf667cbde] soc-2020-info-editor: Add experimental support for catching python stdin/out
Mateusz GrzeliÅski
noreply at git.blender.org
Thu Jul 2 15:23:23 CEST 2020
Commit: f8cf667cbde6f7abf9ab9c6255818a99ed416bb0
Author: Mateusz Grzeliński
Date: Thu Jul 2 15:04:36 2020 +0200
Branches: soc-2020-info-editor
https://developer.blender.org/rBf8cf667cbde6f7abf9ab9c6255818a99ed416bb0
Add experimental support for catching python stdin/out
Python evaluation logs will be printed as info report
===================================================================
M source/blender/python/intern/CMakeLists.txt
M source/blender/python/intern/bpy_interface.c
A source/blender/python/intern/bpy_interface_inoutwrapper.c
A source/blender/python/intern/bpy_interface_inoutwrapper.h
===================================================================
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 769618005af..31a8289dfa4 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
bpy_gizmo_wrap.c
bpy_interface.c
bpy_interface_atexit.c
+ bpy_interface_inoutwrapper.c
bpy_intern_string.c
bpy_library_load.c
bpy_library_write.c
@@ -101,6 +102,7 @@ set(SRC
bpy_capi_utils.h
bpy_driver.h
bpy_gizmo_wrap.h
+ bpy_interface_inoutwrapper.h
bpy_intern_string.h
bpy_library.h
bpy_msgbus.h
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 6da1715b02d..099190155cb 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -24,6 +24,7 @@
* be accesses from scripts.
*/
+#include <BKE_report.h>
#include <Python.h>
#include "MEM_guardedalloc.h"
@@ -42,6 +43,7 @@
#include "bpy.h"
#include "bpy_capi_utils.h"
+#include "bpy_interface_inoutwrapper.h"
#include "bpy_intern_string.h"
#include "bpy_path.h"
#include "bpy_rna.h"
@@ -246,6 +248,7 @@ static struct _inittab bpy_internal_modules[] = {
#endif
{"gpu", BPyInit_gpu},
{"idprop", BPyInit_idprop},
+ {"_inoutwrapper", PyInit_in_out_wrapper},
{NULL, NULL},
};
@@ -494,9 +497,15 @@ static bool python_script_exec(
}
}
+ char *output;
if (text->compiled) {
+ BPY_intern_init_inoutwrapper();
py_dict = PyC_DefaultNameSpace(fn_dummy);
py_result = PyEval_EvalCode(text->compiled, py_dict, py_dict);
+ output = BPY_intern_get_inout_buffer();
+ BKE_report(reports, RPT_INFO, output);
+ BPY_intern_free_inoutwrapper();
+ MEM_freeN(output);
}
}
else {
diff --git a/source/blender/python/intern/bpy_interface_inoutwrapper.c b/source/blender/python/intern/bpy_interface_inoutwrapper.c
new file mode 100644
index 00000000000..a0f837ab422
--- /dev/null
+++ b/source/blender/python/intern/bpy_interface_inoutwrapper.c
@@ -0,0 +1,234 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ *
+ * This file wraps around pythons stdin stdout and exposes it in buffer
+ */
+
+#include <BLI_dynstr.h>
+#include <Python.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "CLG_log.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_string_utf8.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "RNA_types.h"
+
+#include "bpy.h"
+#include "bpy_capi_utils.h"
+#include "bpy_intern_string.h"
+#include "bpy_path.h"
+#include "bpy_rna.h"
+#include "bpy_traceback.h"
+
+#include "bpy_app_translations.h"
+
+#include "DNA_text_types.h"
+
+#include "BKE_appdir.h"
+#include "BKE_context.h"
+#include "BKE_global.h" /* only for script checking */
+#include "BKE_main.h"
+#include "BKE_text.h"
+#include "bpy_interface_inoutwrapper.h"
+
+// todo avoid single global buffer
+static DynStr *io_buffer = NULL;
+
+// Internal state
+PyObject *g_stdout;
+PyObject *g_stdout_saved;
+PyObject *g_stderr;
+PyObject *g_stderr_saved;
+
+// instead of printing to stdout, use this function
+typedef size_t (*print_handle)(char *);
+
+typedef struct InOutWrapper {
+ PyObject_HEAD print_handle write;
+} InOutWrapper;
+
+static PyObject *Stdout_write(PyObject *self, PyObject *args)
+{
+ size_t written = 0;
+ InOutWrapper *selfimpl = (InOutWrapper *)(self);
+ if (selfimpl->write) {
+ char *data;
+ if (!PyArg_ParseTuple(args, "s", &data))
+ return 0;
+
+ written = selfimpl->write(data);
+ }
+ return PyLong_FromSize_t(written);
+}
+
+static PyObject *Stdout_flush(PyObject *UNUSED(self), PyObject *UNUSED(args))
+{
+ // no-op
+ return Py_BuildValue("");
+}
+
+PyMethodDef InOut_methods[] = {
+ {"write", Stdout_write, METH_VARARGS, "sys.stdout.write"},
+ {"flush", Stdout_flush, METH_VARARGS, "sys.stdout.write"},
+ {0, 0, 0, 0} // sentinel
+};
+
+PyTypeObject InOutHandlerType = {
+ PyVarObject_HEAD_INIT(0, 0) "InOutHandlerType", /* tp_name */
+ sizeof(InOutWrapper), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "InOutWrapper objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ InOut_methods, /* tp_methods */
+};
+
+PyModuleDef in_out_wrapper_module = {
+ PyModuleDef_HEAD_INIT,
+ "_in_out_wrapper",
+ 0,
+ -1,
+ 0,
+};
+
+PyMODINIT_FUNC PyInit_in_out_wrapper(void)
+{
+ g_stdout = NULL;
+ g_stdout_saved = NULL;
+
+ InOutHandlerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&InOutHandlerType) < 0)
+ return 0;
+
+ PyObject *m = PyModule_Create(&in_out_wrapper_module);
+ if (m) {
+ Py_INCREF(&InOutHandlerType);
+ PyModule_AddObject(m, "InOutWrapper", (PyObject *)(&InOutHandlerType));
+ }
+ return m;
+}
+
+static void set_stdout(print_handle write)
+{
+ if (!g_stdout) {
+ g_stdout_saved = PySys_GetObject("stdout"); // borrowed
+ g_stdout = InOutHandlerType.tp_new(&InOutHandlerType, NULL, NULL);
+ }
+
+ InOutWrapper *impl = (InOutWrapper *)(g_stdout);
+ impl->write = write;
+ PySys_SetObject("stdout", g_stdout);
+}
+
+static void set_stderr(print_handle write)
+{
+ if (!g_stderr) {
+ g_stderr_saved = PySys_GetObject("stderr"); // borrowed
+ g_stderr = InOutHandlerType.tp_new(&InOutHandlerType, NULL, NULL);
+ }
+
+ InOutWrapper *impl = (InOutWrapper *)(g_stderr);
+ impl->write = write;
+ PySys_SetObject("stderr", g_stdout);
+}
+
+// todo investigate if not calling reset causes memory leak
+static void reset_stdout(void)
+{
+ if (g_stdout_saved)
+ PySys_SetObject("stdout", g_stdout_saved);
+
+ Py_XDECREF(g_stdout);
+ g_stdout = 0;
+}
+
+static void reset_stderr(void)
+{
+ if (g_stdout_saved)
+ PySys_SetObject("stderr", g_stderr_saved);
+
+ Py_XDECREF(g_stdout);
+ g_stdout = 0;
+}
+
+// todo there is no use for returning written bytes
+static size_t custom_write(char *input)
+{
+ int len = BLI_dynstr_get_len(io_buffer);
+ BLI_dynstr_append(io_buffer, input);
+ printf("custom write> %s", input);
+ return BLI_dynstr_get_len(io_buffer) - len;
+}
+
+// todo investigate possible conflicts with BPy_reports_write_stdout
+/* use it anywhere after Py_Initialize */
+void BPY_intern_init_inoutwrapper()
+{
+ PyImport_ImportModule("_inoutwrapper");
+ BLI_assert(io_buffer == NULL);
+ io_buffer = BLI_dynstr_new();
+
+ // switch sys.stdout to custom handler
+ print_handle stdout_write_hadle = custom_write;
+ set_stdout(stdout_write_hadle);
+ set_stderr(stdout_write_hadle);
+}
+
+char *BPY_intern_get_inout_buffer()
+{
+ BLI_assert(io_buffer != NULL);
+ return BLI_dynstr_get_cstring(io_buffer);
+}
+
+void BPY_intern_free_inoutwrapper()
+{
+ BLI_assert(io_buffer != NULL);
+ BLI_dynstr_free(io_buffer);
+ io_buffer = NULL;
+ reset_stderr();
+ reset_stdout();
+}
diff --git a/source/blender/python/intern/bpy_interface_inoutwrapper.h b/source/blender/python/intern/bpy_interface_inoutwrapper.h
new file mode 100644
index 00000000000..0880dd062e2
--- /dev/null
+++ b/source/blender/python/intern/bpy_interface_inoutwrapper.h
@@ -0,0 +1,25 @@
+/*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software Foundation,
+* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list