978 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			978 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * ***** BEGIN GPL LICENSE BLOCK *****
 | 
						|
 *
 | 
						|
 * 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.
 | 
						|
 *
 | 
						|
 * ***** END GPL LICENSE BLOCK *****
 | 
						|
 */
 | 
						|
 | 
						|
/** \file blender/python/generic/py_capi_utils.c
 | 
						|
 *  \ingroup pygen
 | 
						|
 *
 | 
						|
 * Extend upon CPython's API, filling in some gaps, these functions use PyC_
 | 
						|
 * prefix to distinguish them apart from CPython.
 | 
						|
 *
 | 
						|
 * \note
 | 
						|
 * This module should only depend on CPython, however it currently uses
 | 
						|
 * BLI_string_utf8() for unicode conversion.
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
#include <Python.h>
 | 
						|
#include <frameobject.h>
 | 
						|
 | 
						|
#include "BLI_utildefines.h"  /* for bool */
 | 
						|
 | 
						|
#include "py_capi_utils.h"
 | 
						|
 | 
						|
/* only for BLI_strncpy_wchar_from_utf8, should replace with py funcs but too late in release now */
 | 
						|
#include "BLI_string_utf8.h"
 | 
						|
 | 
						|
#ifdef _WIN32
 | 
						|
#include "BLI_path_util.h"  /* BLI_setenv() */
 | 
						|
#include "BLI_math_base.h"  /* finite() */
 | 
						|
#endif
 | 
						|
 | 
						|
/* array utility function */
 | 
						|
int PyC_AsArray(void *array, PyObject *value, const Py_ssize_t length,
 | 
						|
                const PyTypeObject *type, const bool is_double, const char *error_prefix)
 | 
						|
{
 | 
						|
	PyObject *value_fast;
 | 
						|
	Py_ssize_t value_len;
 | 
						|
	Py_ssize_t i;
 | 
						|
 | 
						|
	if (!(value_fast = PySequence_Fast(value, error_prefix))) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	value_len = PySequence_Fast_GET_SIZE(value_fast);
 | 
						|
 | 
						|
	if (value_len != length) {
 | 
						|
		Py_DECREF(value);
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "%.200s: invalid sequence length. expected %d, got %d",
 | 
						|
		             error_prefix, length, value_len);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* for each type */
 | 
						|
	if (type == &PyFloat_Type) {
 | 
						|
		if (is_double) {
 | 
						|
			double *array_double = array;
 | 
						|
			for (i = 0; i < length; i++) {
 | 
						|
				array_double[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i));
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			float *array_float = array;
 | 
						|
			for (i = 0; i < length; i++) {
 | 
						|
				array_float[i] = PyFloat_AsDouble(PySequence_Fast_GET_ITEM(value_fast, i));
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (type == &PyLong_Type) {
 | 
						|
		/* could use is_double for 'long int' but no use now */
 | 
						|
		int *array_int = array;
 | 
						|
		for (i = 0; i < length; i++) {
 | 
						|
			array_int[i] = PyLong_AsLong(PySequence_Fast_GET_ITEM(value_fast, i));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (type == &PyBool_Type) {
 | 
						|
		int *array_bool = array;
 | 
						|
		for (i = 0; i < length; i++) {
 | 
						|
			array_bool[i] = (PyLong_AsLong(PySequence_Fast_GET_ITEM(value_fast, i)) != 0);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		Py_DECREF(value_fast);
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "%s: internal error %s is invalid",
 | 
						|
		             error_prefix, type->tp_name);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	Py_DECREF(value_fast);
 | 
						|
 | 
						|
	if (PyErr_Occurred()) {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "%s: one or more items could not be used as a %s",
 | 
						|
		             error_prefix, type->tp_name);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* array utility function */
 | 
						|
PyObject *PyC_FromArray(const void *array, int length, const PyTypeObject *type,
 | 
						|
                        const bool is_double, const char *error_prefix)
 | 
						|
{
 | 
						|
	PyObject *tuple;
 | 
						|
	int i;
 | 
						|
 | 
						|
	tuple = PyTuple_New(length);
 | 
						|
 | 
						|
	/* for each type */
 | 
						|
	if (type == &PyFloat_Type) {
 | 
						|
		if (is_double) {
 | 
						|
			const double *array_double = array;
 | 
						|
			for (i = 0; i < length; ++i) {
 | 
						|
				PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array_double[i]));
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			const float *array_float = array;
 | 
						|
			for (i = 0; i < length; ++i) {
 | 
						|
				PyTuple_SET_ITEM(tuple, i, PyFloat_FromDouble(array_float[i]));
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (type == &PyLong_Type) {
 | 
						|
		/* could use is_double for 'long int' but no use now */
 | 
						|
		const int *array_int = array;
 | 
						|
		for (i = 0; i < length; ++i) {
 | 
						|
			PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(array_int[i]));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (type == &PyBool_Type) {
 | 
						|
		const int *array_bool = array;
 | 
						|
		for (i = 0; i < length; ++i) {
 | 
						|
			PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(array_bool[i]));
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		Py_DECREF(tuple);
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "%s: internal error %s is invalid",
 | 
						|
		             error_prefix, type->tp_name);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return tuple;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Caller needs to ensure tuple is uninitialized.
 | 
						|
 * Handy for filling a typle with None for eg.
 | 
						|
 */
 | 
						|
void PyC_Tuple_Fill(PyObject *tuple, PyObject *value)
 | 
						|
{
 | 
						|
	unsigned int tot = PyTuple_GET_SIZE(tuple);
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	for (i = 0; i < tot; i++) {
 | 
						|
		PyTuple_SET_ITEM(tuple, i, value);
 | 
						|
		Py_INCREF(value);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* for debugging */
 | 
						|
void PyC_ObSpit(const char *name, PyObject *var)
 | 
						|
{
 | 
						|
	fprintf(stderr, "<%s> : ", name);
 | 
						|
	if (var == NULL) {
 | 
						|
		fprintf(stderr, "<NIL>");
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		PyObject_Print(var, stderr, 0);
 | 
						|
		fprintf(stderr, " ref:%d ", (int)var->ob_refcnt);
 | 
						|
		fprintf(stderr, " ptr:%p", (void *)var);
 | 
						|
		
 | 
						|
		fprintf(stderr, " type:");
 | 
						|
		if (Py_TYPE(var))
 | 
						|
			fprintf(stderr, "%s", Py_TYPE(var)->tp_name);
 | 
						|
		else
 | 
						|
			fprintf(stderr, "<NIL>");
 | 
						|
	}
 | 
						|
	fprintf(stderr, "\n");
 | 
						|
}
 | 
						|
 | 
						|
void PyC_LineSpit(void)
 | 
						|
{
 | 
						|
 | 
						|
	const char *filename;
 | 
						|
	int lineno;
 | 
						|
 | 
						|
	/* Note, allow calling from outside python (RNA) */
 | 
						|
	if (!PyC_IsInterpreterActive()) {
 | 
						|
		fprintf(stderr, "python line lookup failed, interpreter inactive\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	PyErr_Clear();
 | 
						|
	PyC_FileAndNum(&filename, &lineno);
 | 
						|
	
 | 
						|
	fprintf(stderr, "%s:%d\n", filename, lineno);
 | 
						|
}
 | 
						|
 | 
						|
void PyC_StackSpit(void)
 | 
						|
{
 | 
						|
	/* Note, allow calling from outside python (RNA) */
 | 
						|
	if (!PyC_IsInterpreterActive()) {
 | 
						|
		fprintf(stderr, "python line lookup failed, interpreter inactive\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/* lame but handy */
 | 
						|
		PyGILState_STATE gilstate = PyGILState_Ensure();
 | 
						|
		PyRun_SimpleString("__import__('traceback').print_stack()");
 | 
						|
		PyGILState_Release(gilstate);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void PyC_FileAndNum(const char **filename, int *lineno)
 | 
						|
{
 | 
						|
	PyFrameObject *frame;
 | 
						|
	
 | 
						|
	if (filename) *filename = NULL;
 | 
						|
	if (lineno)   *lineno = -1;
 | 
						|
 | 
						|
	if (!(frame = PyThreadState_GET()->frame)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* when executing a script */
 | 
						|
	if (filename) {
 | 
						|
		*filename = _PyUnicode_AsString(frame->f_code->co_filename);
 | 
						|
	}
 | 
						|
 | 
						|
	/* when executing a module */
 | 
						|
	if (filename && *filename == NULL) {
 | 
						|
		/* try an alternative method to get the filename - module based
 | 
						|
		 * references below are all borrowed (double checked) */
 | 
						|
		PyObject *mod_name = PyDict_GetItemString(PyEval_GetGlobals(), "__name__");
 | 
						|
		if (mod_name) {
 | 
						|
			PyObject *mod = PyDict_GetItem(PyImport_GetModuleDict(), mod_name);
 | 
						|
			if (mod) {
 | 
						|
				*filename = PyModule_GetFilename(mod);
 | 
						|
			}
 | 
						|
 | 
						|
			/* unlikely, fallback */
 | 
						|
			if (*filename == NULL) {
 | 
						|
				*filename = _PyUnicode_AsString(mod_name);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (lineno) {
 | 
						|
		*lineno = PyFrame_GetLineNumber(frame);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void PyC_FileAndNum_Safe(const char **filename, int *lineno)
 | 
						|
{
 | 
						|
	if (!PyC_IsInterpreterActive()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	PyC_FileAndNum(filename, lineno);
 | 
						|
}
 | 
						|
 | 
						|
/* Would be nice if python had this built in */
 | 
						|
PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...)
 | 
						|
{
 | 
						|
	Py_ssize_t i;
 | 
						|
	PyObject *item = o;
 | 
						|
	const char *attr;
 | 
						|
	
 | 
						|
	va_list vargs;
 | 
						|
 | 
						|
	va_start(vargs, n);
 | 
						|
	for (i = 0; i < n; i++) {
 | 
						|
		attr = va_arg(vargs, char *);
 | 
						|
		item = PyObject_GetAttrString(item, attr);
 | 
						|
		
 | 
						|
		if (item) 
 | 
						|
			Py_DECREF(item);
 | 
						|
		else /* python will set the error value here */
 | 
						|
			break;
 | 
						|
		
 | 
						|
	}
 | 
						|
	va_end(vargs);
 | 
						|
	
 | 
						|
	Py_XINCREF(item); /* final value has is increfed, to match PyObject_GetAttrString */
 | 
						|
	return item;
 | 
						|
}
 | 
						|
 | 
						|
PyObject *PyC_FrozenSetFromStrings(const char **strings)
 | 
						|
{
 | 
						|
	const char **str;
 | 
						|
	PyObject *ret;
 | 
						|
 | 
						|
	ret = PyFrozenSet_New(NULL);
 | 
						|
 | 
						|
	for (str = strings; *str; str++) {
 | 
						|
		PyObject *py_str = PyUnicode_FromString(*str);
 | 
						|
		PySet_Add(ret, py_str);
 | 
						|
		Py_DECREF(py_str);
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* similar to PyErr_Format(),
 | 
						|
 *
 | 
						|
 * implementation - we cant actually preprend the existing exception,
 | 
						|
 * because it could have _any_ arguments given to it, so instead we get its
 | 
						|
 * __str__ output and raise our own exception including it.
 | 
						|
 */
 | 
						|
PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...)
 | 
						|
{
 | 
						|
	PyObject *error_value_prefix;
 | 
						|
	va_list args;
 | 
						|
 | 
						|
	va_start(args, format);
 | 
						|
	error_value_prefix = PyUnicode_FromFormatV(format, args); /* can fail and be NULL */
 | 
						|
	va_end(args);
 | 
						|
 | 
						|
	if (PyErr_Occurred()) {
 | 
						|
		PyObject *error_type, *error_value, *error_traceback;
 | 
						|
		PyErr_Fetch(&error_type, &error_value, &error_traceback);
 | 
						|
		PyErr_Format(exception_type_prefix,
 | 
						|
		             "%S, %.200s(%S)",
 | 
						|
		             error_value_prefix,
 | 
						|
		             Py_TYPE(error_value)->tp_name,
 | 
						|
		             error_value
 | 
						|
		             );
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		PyErr_SetObject(exception_type_prefix,
 | 
						|
		                error_value_prefix
 | 
						|
		                );
 | 
						|
	}
 | 
						|
 | 
						|
	Py_XDECREF(error_value_prefix);
 | 
						|
 | 
						|
	/* dumb to always return NULL but matches PyErr_Format */
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* returns the exception string as a new PyUnicode object, depends on external traceback module */
 | 
						|
#if 0
 | 
						|
 | 
						|
/* this version uses traceback module but somehow fails on UI errors */
 | 
						|
 | 
						|
PyObject *PyC_ExceptionBuffer(void)
 | 
						|
{
 | 
						|
	PyObject *traceback_mod = NULL;
 | 
						|
	PyObject *format_tb_func = NULL;
 | 
						|
	PyObject *ret = NULL;
 | 
						|
 | 
						|
	if (!(traceback_mod = PyImport_ImportModule("traceback"))) {
 | 
						|
		goto error_cleanup;
 | 
						|
	}
 | 
						|
	else if (!(format_tb_func = PyObject_GetAttrString(traceback_mod, "format_exc"))) {
 | 
						|
		goto error_cleanup;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = PyObject_CallObject(format_tb_func, NULL);
 | 
						|
 | 
						|
	if (ret == Py_None) {
 | 
						|
		Py_DECREF(ret);
 | 
						|
		ret = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
error_cleanup:
 | 
						|
	/* could not import the module so print the error and close */
 | 
						|
	Py_XDECREF(traceback_mod);
 | 
						|
	Py_XDECREF(format_tb_func);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
#else /* verbose, non-threadsafe version */
 | 
						|
PyObject *PyC_ExceptionBuffer(void)
 | 
						|
{
 | 
						|
	PyObject *stdout_backup = PySys_GetObject("stdout"); /* borrowed */
 | 
						|
	PyObject *stderr_backup = PySys_GetObject("stderr"); /* borrowed */
 | 
						|
	PyObject *string_io = NULL;
 | 
						|
	PyObject *string_io_buf = NULL;
 | 
						|
	PyObject *string_io_mod = NULL;
 | 
						|
	PyObject *string_io_getvalue = NULL;
 | 
						|
 | 
						|
	PyObject *error_type, *error_value, *error_traceback;
 | 
						|
 | 
						|
	if (!PyErr_Occurred())
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	PyErr_Fetch(&error_type, &error_value, &error_traceback);
 | 
						|
 | 
						|
	PyErr_Clear();
 | 
						|
 | 
						|
	/* import io
 | 
						|
	 * string_io = io.StringIO()
 | 
						|
	 */
 | 
						|
 | 
						|
	if (!(string_io_mod = PyImport_ImportModule("io"))) {
 | 
						|
		goto error_cleanup;
 | 
						|
	}
 | 
						|
	else if (!(string_io = PyObject_CallMethod(string_io_mod, "StringIO", NULL))) {
 | 
						|
		goto error_cleanup;
 | 
						|
	}
 | 
						|
	else if (!(string_io_getvalue = PyObject_GetAttrString(string_io, "getvalue"))) {
 | 
						|
		goto error_cleanup;
 | 
						|
	}
 | 
						|
 | 
						|
	Py_INCREF(stdout_backup); // since these were borrowed we don't want them freed when replaced.
 | 
						|
	Py_INCREF(stderr_backup);
 | 
						|
 | 
						|
	PySys_SetObject("stdout", string_io); // both of these are freed when restoring
 | 
						|
	PySys_SetObject("stderr", string_io);
 | 
						|
 | 
						|
	PyErr_Restore(error_type, error_value, error_traceback);
 | 
						|
	PyErr_Print(); /* print the error */
 | 
						|
	PyErr_Clear();
 | 
						|
 | 
						|
	string_io_buf = PyObject_CallObject(string_io_getvalue, NULL);
 | 
						|
 | 
						|
	PySys_SetObject("stdout", stdout_backup);
 | 
						|
	PySys_SetObject("stderr", stderr_backup);
 | 
						|
 | 
						|
	Py_DECREF(stdout_backup); /* now sys owns the ref again */
 | 
						|
	Py_DECREF(stderr_backup);
 | 
						|
 | 
						|
	Py_DECREF(string_io_mod);
 | 
						|
	Py_DECREF(string_io_getvalue);
 | 
						|
	Py_DECREF(string_io); /* free the original reference */
 | 
						|
 | 
						|
	PyErr_Clear();
 | 
						|
	return string_io_buf;
 | 
						|
 | 
						|
 | 
						|
error_cleanup:
 | 
						|
	/* could not import the module so print the error and close */
 | 
						|
	Py_XDECREF(string_io_mod);
 | 
						|
	Py_XDECREF(string_io);
 | 
						|
 | 
						|
	PyErr_Restore(error_type, error_value, error_traceback);
 | 
						|
	PyErr_Print(); /* print the error */
 | 
						|
	PyErr_Clear();
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* string conversion, escape non-unicode chars, coerce must be set to NULL */
 | 
						|
const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce)
 | 
						|
{
 | 
						|
	const char *result;
 | 
						|
 | 
						|
	result = _PyUnicode_AsString(py_str);
 | 
						|
 | 
						|
	if (result) {
 | 
						|
		/* 99% of the time this is enough but we better support non unicode
 | 
						|
		 * chars since blender doesnt limit this */
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		PyErr_Clear();
 | 
						|
 | 
						|
		if (PyBytes_Check(py_str)) {
 | 
						|
			return PyBytes_AS_STRING(py_str);
 | 
						|
		}
 | 
						|
#ifdef WIN32
 | 
						|
		/* bug [#31856] oddly enough, Python3.2 --> 3.3 on Windows will throw an
 | 
						|
		 * exception here this needs to be fixed in python:
 | 
						|
		 * see: bugs.python.org/issue15859 */
 | 
						|
		else if (!PyUnicode_Check(py_str)) {
 | 
						|
			PyErr_BadArgument();
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
		else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) {
 | 
						|
			return PyBytes_AS_STRING(*coerce);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			/* leave error raised from EncodeFS */
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
PyObject *PyC_UnicodeFromByteAndSize(const char *str, Py_ssize_t size)
 | 
						|
{
 | 
						|
	PyObject *result = PyUnicode_FromStringAndSize(str, size);
 | 
						|
	if (result) {
 | 
						|
		/* 99% of the time this is enough but we better support non unicode
 | 
						|
		 * chars since blender doesnt limit this */
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		PyErr_Clear();
 | 
						|
		/* this means paths will always be accessible once converted, on all OS's */
 | 
						|
		result = PyUnicode_DecodeFSDefaultAndSize(str, size);
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
PyObject *PyC_UnicodeFromByte(const char *str)
 | 
						|
{
 | 
						|
	return PyC_UnicodeFromByteAndSize(str, strlen(str));
 | 
						|
}
 | 
						|
 | 
						|
/*****************************************************************************
 | 
						|
 * Description: This function creates a new Python dictionary object.
 | 
						|
 * note: dict is owned by sys.modules["__main__"] module, reference is borrowed
 | 
						|
 * note: important we use the dict from __main__, this is what python expects
 | 
						|
 *  for 'pickle' to work as well as strings like this...
 | 
						|
 * >> foo = 10
 | 
						|
 * >> print(__import__("__main__").foo)
 | 
						|
 *
 | 
						|
 * note: this overwrites __main__ which gives problems with nested calls.
 | 
						|
 * be sure to run PyC_MainModule_Backup & PyC_MainModule_Restore if there is
 | 
						|
 * any chance that python is in the call stack.
 | 
						|
 ****************************************************************************/
 | 
						|
PyObject *PyC_DefaultNameSpace(const char *filename)
 | 
						|
{
 | 
						|
	PyInterpreterState *interp = PyThreadState_GET()->interp;
 | 
						|
	PyObject *mod_main = PyModule_New("__main__");
 | 
						|
	PyDict_SetItemString(interp->modules, "__main__", mod_main);
 | 
						|
	Py_DECREF(mod_main); /* sys.modules owns now */
 | 
						|
	PyModule_AddStringConstant(mod_main, "__name__", "__main__");
 | 
						|
	if (filename) {
 | 
						|
		/* __file__ mainly for nice UI'ness
 | 
						|
		 * note: this wont map to a real file when executing text-blocks and buttons. */
 | 
						|
		PyModule_AddObject(mod_main, "__file__", PyC_UnicodeFromByte(filename));
 | 
						|
	}
 | 
						|
	PyModule_AddObject(mod_main, "__builtins__", interp->builtins);
 | 
						|
	Py_INCREF(interp->builtins); /* AddObject steals a reference */
 | 
						|
	return PyModule_GetDict(mod_main);
 | 
						|
}
 | 
						|
 | 
						|
/* restore MUST be called after this */
 | 
						|
void PyC_MainModule_Backup(PyObject **main_mod)
 | 
						|
{
 | 
						|
	PyInterpreterState *interp = PyThreadState_GET()->interp;
 | 
						|
	*main_mod = PyDict_GetItemString(interp->modules, "__main__");
 | 
						|
	Py_XINCREF(*main_mod); /* don't free */
 | 
						|
}
 | 
						|
 | 
						|
void PyC_MainModule_Restore(PyObject *main_mod)
 | 
						|
{
 | 
						|
	PyInterpreterState *interp = PyThreadState_GET()->interp;
 | 
						|
	PyDict_SetItemString(interp->modules, "__main__", main_mod);
 | 
						|
	Py_XDECREF(main_mod);
 | 
						|
}
 | 
						|
 | 
						|
/* must be called before Py_Initialize, expects output of BLI_get_folder(BLENDER_PYTHON, NULL) */
 | 
						|
void PyC_SetHomePath(const char *py_path_bundle)
 | 
						|
{
 | 
						|
	if (py_path_bundle == NULL) {
 | 
						|
		/* Common enough to have bundled *nix python but complain on OSX/Win */
 | 
						|
#if defined(__APPLE__) || defined(_WIN32)
 | 
						|
		fprintf(stderr, "Warning! bundled python not found and is expected on this platform. "
 | 
						|
		        "(if you built with CMake: 'install' target may have not been built)\n");
 | 
						|
#endif
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	/* set the environment path */
 | 
						|
	printf("found bundled python: %s\n", py_path_bundle);
 | 
						|
 | 
						|
#ifdef __APPLE__
 | 
						|
	/* OSX allow file/directory names to contain : character (represented as / in the Finder)
 | 
						|
	 * but current Python lib (release 3.1.1) doesn't handle these correctly */
 | 
						|
	if (strchr(py_path_bundle, ':'))
 | 
						|
		printf("Warning : Blender application is located in a path containing : or / chars\
 | 
						|
		       \nThis may make python import function fail\n");
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
#if 0 /* disable for now [#31506] - campbell */
 | 
						|
#ifdef _WIN32
 | 
						|
	/* cmake/MSVC debug build crashes without this, why only
 | 
						|
	 * in this case is unknown.. */
 | 
						|
	{
 | 
						|
		/*BLI_setenv("PYTHONPATH", py_path_bundle)*/;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
	{
 | 
						|
		static wchar_t py_path_bundle_wchar[1024];
 | 
						|
 | 
						|
		/* cant use this, on linux gives bug: #23018, TODO: try LANG="en_US.UTF-8" /usr/bin/blender, suggested 22008 */
 | 
						|
		/* mbstowcs(py_path_bundle_wchar, py_path_bundle, FILE_MAXDIR); */
 | 
						|
 | 
						|
		BLI_strncpy_wchar_from_utf8(py_path_bundle_wchar, py_path_bundle, ARRAY_SIZE(py_path_bundle_wchar));
 | 
						|
 | 
						|
		Py_SetPythonHome(py_path_bundle_wchar);
 | 
						|
		// printf("found python (wchar_t) '%ls'\n", py_path_bundle_wchar);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool PyC_IsInterpreterActive(void)
 | 
						|
{
 | 
						|
	return (((PyThreadState *)_Py_atomic_load_relaxed(&_PyThreadState_Current)) != NULL);
 | 
						|
}
 | 
						|
 | 
						|
/* Would be nice if python had this built in
 | 
						|
 * See: http://wiki.blender.org/index.php/Dev:Doc/Tools/Debugging/PyFromC
 | 
						|
 */
 | 
						|
void PyC_RunQuicky(const char *filepath, int n, ...)
 | 
						|
{
 | 
						|
	FILE *fp = fopen(filepath, "r");
 | 
						|
 | 
						|
	if (fp) {
 | 
						|
		PyGILState_STATE gilstate = PyGILState_Ensure();
 | 
						|
 | 
						|
		va_list vargs;
 | 
						|
 | 
						|
		int *sizes = PyMem_MALLOC(sizeof(int) * (n / 2));
 | 
						|
		int i;
 | 
						|
 | 
						|
		PyObject *py_dict = PyC_DefaultNameSpace(filepath);
 | 
						|
		PyObject *values = PyList_New(n / 2); /* namespace owns this, don't free */
 | 
						|
 | 
						|
		PyObject *py_result, *ret;
 | 
						|
 | 
						|
		PyObject *struct_mod = PyImport_ImportModule("struct");
 | 
						|
		PyObject *calcsize = PyObject_GetAttrString(struct_mod, "calcsize"); /* struct.calcsize */
 | 
						|
		PyObject *pack = PyObject_GetAttrString(struct_mod, "pack"); /* struct.pack */
 | 
						|
		PyObject *unpack = PyObject_GetAttrString(struct_mod, "unpack"); /* struct.unpack */
 | 
						|
 | 
						|
		Py_DECREF(struct_mod);
 | 
						|
 | 
						|
		va_start(vargs, n);
 | 
						|
		for (i = 0; i * 2 < n; i++) {
 | 
						|
			const char *format = va_arg(vargs, char *);
 | 
						|
			void *ptr = va_arg(vargs, void *);
 | 
						|
 | 
						|
			ret = PyObject_CallFunction(calcsize, "s", format);
 | 
						|
 | 
						|
			if (ret) {
 | 
						|
				sizes[i] = PyLong_AsLong(ret);
 | 
						|
				Py_DECREF(ret);
 | 
						|
				ret = PyObject_CallFunction(unpack, "sy#", format, (char *)ptr, sizes[i]);
 | 
						|
			}
 | 
						|
 | 
						|
			if (ret == NULL) {
 | 
						|
				printf("PyC_InlineRun error, line:%d\n", __LINE__);
 | 
						|
				PyErr_Print();
 | 
						|
				PyErr_Clear();
 | 
						|
 | 
						|
				PyList_SET_ITEM(values, i, Py_None); /* hold user */
 | 
						|
				Py_INCREF(Py_None);
 | 
						|
 | 
						|
				sizes[i] = 0;
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				if (PyTuple_GET_SIZE(ret) == 1) {
 | 
						|
					/* convenience, convert single tuples into single values */
 | 
						|
					PyObject *tmp = PyTuple_GET_ITEM(ret, 0);
 | 
						|
					Py_INCREF(tmp);
 | 
						|
					Py_DECREF(ret);
 | 
						|
					ret = tmp;
 | 
						|
				}
 | 
						|
 | 
						|
				PyList_SET_ITEM(values, i, ret); /* hold user */
 | 
						|
			}
 | 
						|
		}
 | 
						|
		va_end(vargs);
 | 
						|
		
 | 
						|
		/* set the value so we can access it */
 | 
						|
		PyDict_SetItemString(py_dict, "values", values);
 | 
						|
 | 
						|
		py_result = PyRun_File(fp, filepath, Py_file_input, py_dict, py_dict);
 | 
						|
 | 
						|
		fclose(fp);
 | 
						|
 | 
						|
		if (py_result) {
 | 
						|
 | 
						|
			/* we could skip this but then only slice assignment would work
 | 
						|
			 * better not be so strict */
 | 
						|
			values = PyDict_GetItemString(py_dict, "values");
 | 
						|
 | 
						|
			if (values && PyList_Check(values)) {
 | 
						|
 | 
						|
				/* don't use the result */
 | 
						|
				Py_DECREF(py_result);
 | 
						|
				py_result = NULL;
 | 
						|
 | 
						|
				/* now get the values back */
 | 
						|
				va_start(vargs, n);
 | 
						|
				for (i = 0; i * 2 < n; i++) {
 | 
						|
					const char *format = va_arg(vargs, char *);
 | 
						|
					void *ptr = va_arg(vargs, void *);
 | 
						|
					
 | 
						|
					PyObject *item;
 | 
						|
					PyObject *item_new;
 | 
						|
					/* prepend the string formatting and remake the tuple */
 | 
						|
					item = PyList_GET_ITEM(values, i);
 | 
						|
					if (PyTuple_CheckExact(item)) {
 | 
						|
						int ofs = PyTuple_GET_SIZE(item);
 | 
						|
						item_new = PyTuple_New(ofs + 1);
 | 
						|
						while (ofs--) {
 | 
						|
							PyObject *member = PyTuple_GET_ITEM(item, ofs);
 | 
						|
							PyTuple_SET_ITEM(item_new, ofs + 1, member);
 | 
						|
							Py_INCREF(member);
 | 
						|
						}
 | 
						|
 | 
						|
						PyTuple_SET_ITEM(item_new, 0, PyUnicode_FromString(format));
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						item_new = Py_BuildValue("sO", format, item);
 | 
						|
					}
 | 
						|
 | 
						|
					ret = PyObject_Call(pack, item_new, NULL);
 | 
						|
 | 
						|
					if (ret) {
 | 
						|
						/* copy the bytes back into memory */
 | 
						|
						memcpy(ptr, PyBytes_AS_STRING(ret), sizes[i]);
 | 
						|
						Py_DECREF(ret);
 | 
						|
					}
 | 
						|
					else {
 | 
						|
						printf("PyC_InlineRun error on arg '%d', line:%d\n", i, __LINE__);
 | 
						|
						PyC_ObSpit("failed converting:", item_new);
 | 
						|
						PyErr_Print();
 | 
						|
						PyErr_Clear();
 | 
						|
					}
 | 
						|
 | 
						|
					Py_DECREF(item_new);
 | 
						|
				}
 | 
						|
				va_end(vargs);
 | 
						|
			}
 | 
						|
			else {
 | 
						|
				printf("PyC_InlineRun error, 'values' not a list, line:%d\n", __LINE__);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			printf("PyC_InlineRun error line:%d\n", __LINE__);
 | 
						|
			PyErr_Print();
 | 
						|
			PyErr_Clear();
 | 
						|
		}
 | 
						|
 | 
						|
		Py_DECREF(calcsize);
 | 
						|
		Py_DECREF(pack);
 | 
						|
		Py_DECREF(unpack);
 | 
						|
 | 
						|
		PyMem_FREE(sizes);
 | 
						|
 | 
						|
		PyGILState_Release(gilstate);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		fprintf(stderr, "%s: '%s' missing\n", __func__, filepath);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* generic function to avoid depending on RNA */
 | 
						|
void *PyC_RNA_AsPointer(PyObject *value, const char *type_name)
 | 
						|
{
 | 
						|
	PyObject *as_pointer;
 | 
						|
	PyObject *pointer;
 | 
						|
 | 
						|
	if (STREQ(Py_TYPE(value)->tp_name, type_name) &&
 | 
						|
	    (as_pointer = PyObject_GetAttrString(value, "as_pointer")) != NULL &&
 | 
						|
	    PyCallable_Check(as_pointer))
 | 
						|
	{
 | 
						|
		void *result = NULL;
 | 
						|
 | 
						|
		/* must be a 'type_name' object */
 | 
						|
		pointer = PyObject_CallObject(as_pointer, NULL);
 | 
						|
		Py_DECREF(as_pointer);
 | 
						|
 | 
						|
		if (!pointer) {
 | 
						|
			PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed");
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
		result = PyLong_AsVoidPtr(pointer);
 | 
						|
		Py_DECREF(pointer);
 | 
						|
		if (!result) {
 | 
						|
			PyErr_SetString(PyExc_SystemError, "value.as_pointer() failed");
 | 
						|
		}
 | 
						|
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "expected '%.200s' type found '%.200s' instead",
 | 
						|
		             type_name, Py_TYPE(value)->tp_name);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* PyC_FlagSet_* functions - so flags/sets can be interchanged in a generic way */
 | 
						|
#include "BLI_dynstr.h"
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
char *PyC_FlagSet_AsString(PyC_FlagSet *item)
 | 
						|
{
 | 
						|
	DynStr *dynstr = BLI_dynstr_new();
 | 
						|
	PyC_FlagSet *e;
 | 
						|
	char *cstring;
 | 
						|
 | 
						|
	for (e = item; item->identifier; item++) {
 | 
						|
		BLI_dynstr_appendf(dynstr, (e == item) ? "'%s'" : ", '%s'", item->identifier);
 | 
						|
	}
 | 
						|
 | 
						|
	cstring = BLI_dynstr_get_cstring(dynstr);
 | 
						|
	BLI_dynstr_free(dynstr);
 | 
						|
	return cstring;
 | 
						|
}
 | 
						|
 | 
						|
int PyC_FlagSet_ValueFromID_int(PyC_FlagSet *item, const char *identifier, int *value)
 | 
						|
{
 | 
						|
	for ( ; item->identifier; item++) {
 | 
						|
		if (STREQ(item->identifier, identifier)) {
 | 
						|
			*value = item->value;
 | 
						|
			return 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int PyC_FlagSet_ValueFromID(PyC_FlagSet *item, const char *identifier, int *value, const char *error_prefix)
 | 
						|
{
 | 
						|
	if (PyC_FlagSet_ValueFromID_int(item, identifier, value) == 0) {
 | 
						|
		const char *enum_str = PyC_FlagSet_AsString(item);
 | 
						|
		PyErr_Format(PyExc_ValueError,
 | 
						|
		             "%s: '%.200s' not found in (%s)",
 | 
						|
		             error_prefix, identifier, enum_str);
 | 
						|
		MEM_freeN((void *)enum_str);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int PyC_FlagSet_ToBitfield(PyC_FlagSet *items, PyObject *value, int *r_value, const char *error_prefix)
 | 
						|
{
 | 
						|
	/* set of enum items, concatenate all values with OR */
 | 
						|
	int ret, flag = 0;
 | 
						|
 | 
						|
	/* set looping */
 | 
						|
	Py_ssize_t pos = 0;
 | 
						|
	Py_ssize_t hash = 0;
 | 
						|
	PyObject *key;
 | 
						|
 | 
						|
	if (!PySet_Check(value)) {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "%.200s expected a set, not %.200s",
 | 
						|
		             error_prefix, Py_TYPE(value)->tp_name);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	*r_value = 0;
 | 
						|
 | 
						|
	while (_PySet_NextEntry(value, &pos, &key, &hash)) {
 | 
						|
		const char *param = _PyUnicode_AsString(key);
 | 
						|
 | 
						|
		if (param == NULL) {
 | 
						|
			PyErr_Format(PyExc_TypeError,
 | 
						|
			             "%.200s set must contain strings, not %.200s",
 | 
						|
			             error_prefix, Py_TYPE(key)->tp_name);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (PyC_FlagSet_ValueFromID(items, param, &ret, error_prefix) < 0) {
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		flag |= ret;
 | 
						|
	}
 | 
						|
 | 
						|
	*r_value = flag;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag)
 | 
						|
{
 | 
						|
	PyObject *ret = PySet_New(NULL);
 | 
						|
	PyObject *pystr;
 | 
						|
 | 
						|
	for ( ; items->identifier; items++) {
 | 
						|
		if (items->value & flag) {
 | 
						|
			pystr = PyUnicode_FromString(items->identifier);
 | 
						|
			PySet_Add(ret, pystr);
 | 
						|
			Py_DECREF(pystr);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * \return -1 on error, else 0
 | 
						|
 *
 | 
						|
 * \note it is caller's responsibility to acquire & release GIL!
 | 
						|
 */
 | 
						|
int PyC_RunString_AsNumber(const char *expr, double *value, const char *filename)
 | 
						|
{
 | 
						|
	PyObject *py_dict, *mod, *retval;
 | 
						|
	int error_ret = 0;
 | 
						|
	PyObject *main_mod = NULL;
 | 
						|
 | 
						|
	PyC_MainModule_Backup(&main_mod);
 | 
						|
 | 
						|
	py_dict = PyC_DefaultNameSpace(filename);
 | 
						|
 | 
						|
	mod = PyImport_ImportModule("math");
 | 
						|
	if (mod) {
 | 
						|
		PyDict_Merge(py_dict, PyModule_GetDict(mod), 0); /* 0 - don't overwrite existing values */
 | 
						|
		Py_DECREF(mod);
 | 
						|
	}
 | 
						|
	else { /* highly unlikely but possibly */
 | 
						|
		PyErr_Print();
 | 
						|
		PyErr_Clear();
 | 
						|
	}
 | 
						|
 | 
						|
	retval = PyRun_String(expr, Py_eval_input, py_dict, py_dict);
 | 
						|
 | 
						|
	if (retval == NULL) {
 | 
						|
		error_ret = -1;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		double val;
 | 
						|
 | 
						|
		if (PyTuple_Check(retval)) {
 | 
						|
			/* Users my have typed in 10km, 2m
 | 
						|
			 * add up all values */
 | 
						|
			int i;
 | 
						|
			val = 0.0;
 | 
						|
 | 
						|
			for (i = 0; i < PyTuple_GET_SIZE(retval); i++) {
 | 
						|
				const double val_item = PyFloat_AsDouble(PyTuple_GET_ITEM(retval, i));
 | 
						|
				if (val_item == -1 && PyErr_Occurred()) {
 | 
						|
					val = -1;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				val += val_item;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			val = PyFloat_AsDouble(retval);
 | 
						|
		}
 | 
						|
		Py_DECREF(retval);
 | 
						|
 | 
						|
		if (val == -1 && PyErr_Occurred()) {
 | 
						|
			error_ret = -1;
 | 
						|
		}
 | 
						|
		else if (!finite(val)) {
 | 
						|
			*value = 0.0;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			*value = val;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	PyC_MainModule_Restore(main_mod);
 | 
						|
 | 
						|
	return error_ret;
 | 
						|
}
 |