Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Eagar <joeedh@gmail.com>2010-01-06 01:33:41 +0300
committerJoseph Eagar <joeedh@gmail.com>2010-01-06 01:33:41 +0300
commit67ff197cb1b0e79a95bf6546b5fe1a481b79fce1 (patch)
tree9f78d5cda71d200cab6475eb9c2747f7181adf3a /source/blender/python
parent473f235a6eee6c02cf41a1e173f53406b62440aa (diff)
parentffe13aeb232ac6bad3a98997b4a352f434293193 (diff)
Merge with trunk/2.5 at r25563
Most likely will not compile for others, I'd appreciate any build errors and missing files reports (I can never seem to get everything committed and all the build systems working without help). Porting over the sculpt/multires tools was a breeze, thanks goes to brecht for a design that didn't exclude ngons and was easy to port. Note that I've not tested externally-backed multires file support yet. Also, I still need to write version patch code for some cases. Some notes: * Like trunk, topological changes don't update multires right, so e.g. subdivide will duplicate multires data on the new faces, instead of subdividing it. * If you set the debug value (ctrl-alt-d) to 1 it'll turn on my experiments in speeding up sculpting on higher-res multires meshes (but note it makes partial redraw not completely accurate). * There's a bug where you have to go through editmode to get out of sculpt mode, not sure if I inherited or created this myself.
Diffstat (limited to 'source/blender/python')
-rw-r--r--source/blender/python/BPY_extern.h2
-rw-r--r--source/blender/python/generic/bpy_internal_import.c3
-rw-r--r--source/blender/python/generic/vector.c73
-rw-r--r--source/blender/python/intern/bpy_array.c97
-rw-r--r--source/blender/python/intern/bpy_driver.c239
-rw-r--r--source/blender/python/intern/bpy_interface.c226
-rw-r--r--source/blender/python/intern/bpy_operator.c18
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.c334
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.h2
-rw-r--r--source/blender/python/intern/bpy_rna.c437
-rw-r--r--source/blender/python/intern/bpy_rna.h1
-rw-r--r--source/blender/python/intern/bpy_util.c18
-rw-r--r--source/blender/python/sphinx_doc_gen.py322
13 files changed, 1360 insertions, 412 deletions
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index a055060ed07..6a94443dd8b 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -134,7 +134,7 @@ extern "C" {
// void BPY_scripts_clear_pyobjects( void );
//
// void error_pyscript( void );
-// void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */
+ void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */
void BPY_set_context(struct bContext *C);
/* void BPY_Err_Handle(struct Text *text); */
/* int BPY_spacetext_is_pywin(struct SpaceText *st); */
diff --git a/source/blender/python/generic/bpy_internal_import.c b/source/blender/python/generic/bpy_internal_import.c
index 957a4e6f52d..2fd44b0f6f5 100644
--- a/source/blender/python/generic/bpy_internal_import.c
+++ b/source/blender/python/generic/bpy_internal_import.c
@@ -74,8 +74,9 @@ PyObject *bpy_text_import( Text *text )
}
}
- len= strlen(text->id.name+2) - 3;
+ len= strlen(text->id.name+2);
strncpy(modulename, text->id.name+2, len);
+ modulename[len - 3]= '\0'; /* remove .py */
return PyImport_ExecCodeModule(modulename, text->compiled);
}
diff --git a/source/blender/python/generic/vector.c b/source/blender/python/generic/vector.c
index 90295d0d3e8..33dcaef5be2 100644
--- a/source/blender/python/generic/vector.c
+++ b/source/blender/python/generic/vector.c
@@ -48,6 +48,7 @@ static PyObject *Vector_Negate( VectorObject * self );
static PyObject *Vector_Resize2D( VectorObject * self );
static PyObject *Vector_Resize3D( VectorObject * self );
static PyObject *Vector_Resize4D( VectorObject * self );
+static PyObject *Vector_ToTuple( VectorObject * self, PyObject *value );
static PyObject *Vector_ToTrackQuat( VectorObject * self, PyObject * args );
static PyObject *Vector_Reflect( VectorObject *self, VectorObject *value );
static PyObject *Vector_Cross( VectorObject * self, VectorObject * value );
@@ -61,6 +62,7 @@ static struct PyMethodDef Vector_methods[] = {
{"resize2D", (PyCFunction) Vector_Resize2D, METH_NOARGS, NULL},
{"resize3D", (PyCFunction) Vector_Resize3D, METH_NOARGS, NULL},
{"resize4D", (PyCFunction) Vector_Resize4D, METH_NOARGS, NULL},
+ {"toTuple", (PyCFunction) Vector_ToTuple, METH_O, NULL},
{"toTrackQuat", ( PyCFunction ) Vector_ToTrackQuat, METH_VARARGS, NULL},
{"reflect", ( PyCFunction ) Vector_Reflect, METH_O, NULL},
{"cross", ( PyCFunction ) Vector_Cross, METH_O, NULL},
@@ -236,6 +238,33 @@ static PyObject *Vector_Resize4D(VectorObject * self)
Py_INCREF(self);
return (PyObject*)self;
}
+
+/*----------------------------Vector.resize4D() ------------------
+ resize the vector to x,y,z,w */
+static PyObject *Vector_ToTuple(VectorObject * self, PyObject *value)
+{
+ int ndigits= PyLong_AsSsize_t(value);
+ int x;
+
+ PyObject *ret;
+
+ if(ndigits > 22 || ndigits < 0) { /* accounts for non ints */
+ PyErr_SetString(PyExc_TypeError, "vector.key(ndigits): ndigits must be between 0 and 21");
+ return NULL;
+ }
+
+ if(!BaseMath_ReadCallback(self))
+ return NULL;
+
+ ret= PyTuple_New(self->size);
+
+ for(x = 0; x < self->size; x++) {
+ PyTuple_SET_ITEM(ret, x, PyFloat_FromDouble(double_round((double)self->vec[x], ndigits)));
+ }
+
+ return ret;
+}
+
/*----------------------------Vector.toTrackQuat(track, up) ----------------------
extract a quaternion from the vector and the track and up axis */
static PyObject *Vector_ToTrackQuat( VectorObject * self, PyObject * args )
@@ -626,36 +655,30 @@ static PyObject *Vector_add(PyObject * v1, PyObject * v2)
static PyObject *Vector_iadd(PyObject * v1, PyObject * v2)
{
int i;
-
VectorObject *vec1 = NULL, *vec2 = NULL;
+
+ if (!VectorObject_Check(v1) || !VectorObject_Check(v2)) {
+ PyErr_SetString(PyExc_AttributeError, "Vector addition: arguments not valid for this operation....\n");
+ return NULL;
+ }
+ vec1 = (VectorObject*)v1;
+ vec2 = (VectorObject*)v2;
- if VectorObject_Check(v1)
- vec1= (VectorObject *)v1;
-
- if VectorObject_Check(v2)
- vec2= (VectorObject *)v2;
-
- /* make sure v1 is always the vector */
- if (vec1 && vec2 ) {
-
- if(!BaseMath_ReadCallback(vec1) || !BaseMath_ReadCallback(vec2))
- return NULL;
-
- /*VECTOR + VECTOR*/
- if(vec1->size != vec2->size) {
- PyErr_SetString(PyExc_AttributeError, "Vector addition: vectors must have the same dimensions for this operation\n");
- return NULL;
- }
- for(i = 0; i < vec1->size; i++) {
- vec1->vec[i] += vec2->vec[i];
- }
- Py_INCREF( v1 );
- return v1;
+ if(vec1->size != vec2->size) {
+ PyErr_SetString(PyExc_AttributeError, "Vector addition: vectors must have the same dimensions for this operation\n");
+ return NULL;
}
+ if(!BaseMath_ReadCallback(vec1) || !BaseMath_ReadCallback(vec2))
+ return NULL;
+
+ for(i = 0; i < vec1->size; i++) {
+ vec1->vec[i] = vec1->vec[i] + vec2->vec[i];
+ }
+
BaseMath_WriteCallback(vec1);
- PyErr_SetString(PyExc_AttributeError, "Vector addition: arguments not valid for this operation....\n");
- return NULL;
+ Py_INCREF( v1 );
+ return v1;
}
/*------------------------obj - obj------------------------------
diff --git a/source/blender/python/intern/bpy_array.c b/source/blender/python/intern/bpy_array.c
index f11c95e7ed5..5f228836b42 100644
--- a/source/blender/python/intern/bpy_array.c
+++ b/source/blender/python/intern/bpy_array.c
@@ -32,8 +32,6 @@
#include "BKE_global.h"
-#include "MEM_guardedalloc.h"
-
#define MAX_ARRAY_DIMENSION 10
typedef void (*ItemConvertFunc)(PyObject *, char *);
@@ -258,7 +256,7 @@ static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *p
if (totitem) {
if (!param_data || RNA_property_flag(prop) & PROP_DYNAMIC)
- data= MEM_callocN(item_size * totitem, "pyrna primitive type array");
+ data= PyMem_MALLOC(item_size * totitem);
else
data= param_data;
@@ -273,7 +271,7 @@ static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *p
else {
/* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */
rna_set_array(ptr, prop, data);
- MEM_freeN(data);
+ PyMem_FREE(data);
}
}
@@ -513,3 +511,94 @@ PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop)
return pyrna_prop_CreatePyObject(ptr, prop);
}
+
+/* TODO, multi-dimensional arrays */
+int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
+{
+ int len= RNA_property_array_length(ptr, prop);
+ int type;
+ int i;
+
+ if(len==0) /* possible with dynamic arrays */
+ return 0;
+
+ if (RNA_property_array_dimension(ptr, prop, NULL) > 1) {
+ PyErr_SetString(PyExc_TypeError, "PropertyRNA - multi dimensional arrays not supported yet");
+ return -1;
+ }
+
+ type= RNA_property_type(prop);
+
+ switch (type) {
+ case PROP_FLOAT:
+ {
+ float value_f= PyFloat_AsDouble(value);
+ if(value_f==-1 && PyErr_Occurred()) {
+ PyErr_Clear();
+ return 0;
+ }
+ else {
+ float tmp[32];
+ float *tmp_arr;
+
+ if(len * sizeof(float) > sizeof(tmp)) {
+ tmp_arr= PyMem_MALLOC(len * sizeof(float));
+ }
+ else {
+ tmp_arr= tmp;
+ }
+
+ RNA_property_float_get_array(ptr, prop, tmp_arr);
+
+ for(i=0; i<len; i++)
+ if(tmp_arr[i] == value_f)
+ break;
+
+ if(tmp_arr != tmp)
+ PyMem_FREE(tmp_arr);
+
+ return i<len ? 1 : 0;
+ }
+ break;
+ }
+ case PROP_BOOLEAN:
+ case PROP_INT:
+ {
+ int value_i= PyLong_AsSsize_t(value);
+ if(value_i==-1 && PyErr_Occurred()) {
+ PyErr_Clear();
+ return 0;
+ }
+ else {
+ int tmp[32];
+ int *tmp_arr;
+
+ if(len * sizeof(int) > sizeof(tmp)) {
+ tmp_arr= PyMem_MALLOC(len * sizeof(int));
+ }
+ else {
+ tmp_arr= tmp;
+ }
+
+ if(type==PROP_BOOLEAN)
+ RNA_property_boolean_get_array(ptr, prop, tmp_arr);
+ else
+ RNA_property_int_get_array(ptr, prop, tmp_arr);
+
+ for(i=0; i<len; i++)
+ if(tmp_arr[i] == value_i)
+ break;
+
+ if(tmp_arr != tmp)
+ PyMem_FREE(tmp_arr);
+
+ return i<len ? 1 : 0;
+ }
+ break;
+ }
+ }
+
+ /* should never reach this */
+ PyErr_SetString(PyExc_TypeError, "PropertyRNA - type not in float/bool/int");
+ return -1;
+}
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
new file mode 100644
index 00000000000..76df28494ac
--- /dev/null
+++ b/source/blender/python/intern/bpy_driver.c
@@ -0,0 +1,239 @@
+/**
+ * $Id:
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contributor(s): Willian P. Germano, Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+/* ****************************************** */
+/* Drivers - PyExpression Evaluation */
+
+#include "DNA_anim_types.h"
+
+#include "BPY_extern.h"
+#include "BKE_fcurve.h"
+
+#include <Python.h>
+
+/* for pydrivers (drivers using one-line Python expressions to express relationships between targets) */
+PyObject *bpy_pydriver_Dict = NULL;
+
+/* For faster execution we keep a special dictionary for pydrivers, with
+ * the needed modules and aliases.
+ */
+static int bpy_pydriver_create_dict(void)
+{
+ PyObject *d, *mod;
+
+ /* validate namespace for driver evaluation */
+ if (bpy_pydriver_Dict) return -1;
+
+ d = PyDict_New();
+ if (d == NULL)
+ return -1;
+ else
+ bpy_pydriver_Dict = d;
+
+ /* import some modules: builtins, bpy, math, (Blender.noise )*/
+ PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins());
+
+ mod = PyImport_ImportModule("math");
+ if (mod) {
+ PyDict_Merge(d, PyModule_GetDict(mod), 0); /* 0 - dont overwrite existing values */
+
+ /* Only keep for backwards compat! - just import all math into root, they are standard */
+ PyDict_SetItemString(d, "math", mod);
+ PyDict_SetItemString(d, "m", mod);
+ Py_DECREF(mod);
+ }
+
+ /* add bpy to global namespace */
+ mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
+ if (mod) {
+ PyDict_SetItemString(bpy_pydriver_Dict, "bpy", mod);
+ Py_DECREF(mod);
+ }
+
+
+#if 0 // non existant yet
+ mod = PyImport_ImportModule("Blender.Noise");
+ if (mod) {
+ PyDict_SetItemString(d, "noise", mod);
+ PyDict_SetItemString(d, "n", mod);
+ Py_DECREF(mod);
+ } else {
+ PyErr_Clear();
+ }
+
+ /* If there's a Blender text called pydrivers.py, import it.
+ * Users can add their own functions to this module.
+ */
+ if (G.f & G_DOSCRIPTLINKS) {
+ mod = importText("pydrivers"); /* can also use PyImport_Import() */
+ if (mod) {
+ PyDict_SetItemString(d, "pydrivers", mod);
+ PyDict_SetItemString(d, "p", mod);
+ Py_DECREF(mod);
+ } else {
+ PyErr_Clear();
+ }
+ }
+#endif // non existant yet
+
+ return 0;
+}
+
+/* Update function, it gets rid of pydrivers global dictionary, forcing
+ * BPY_pydriver_eval to recreate it. This function is used to force
+ * reloading the Blender text module "pydrivers.py", if available, so
+ * updates in it reach pydriver evaluation.
+ */
+void BPY_pydriver_update(void)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
+ PyDict_Clear(bpy_pydriver_Dict);
+ Py_DECREF(bpy_pydriver_Dict);
+ bpy_pydriver_Dict = NULL;
+ }
+
+ PyGILState_Release(gilstate);
+
+ return;
+}
+
+/* error return function for BPY_eval_pydriver */
+static float pydriver_error(ChannelDriver *driver)
+{
+ if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
+ PyDict_Clear(bpy_pydriver_Dict);
+ Py_DECREF(bpy_pydriver_Dict);
+ bpy_pydriver_Dict = NULL;
+ }
+
+ driver->flag |= DRIVER_FLAG_INVALID; /* py expression failed */
+ fprintf(stderr, "\nError in Driver: The following Python expression failed:\n\t'%s'\n\n", driver->expression);
+
+ // BPy_errors_to_report(NULL); // TODO - reports
+ PyErr_Print();
+ PyErr_Clear();
+
+ return 0.0f;
+}
+
+/* This evals py driver expressions, 'expr' is a Python expression that
+ * should evaluate to a float number, which is returned.
+ */
+float BPY_pydriver_eval (ChannelDriver *driver)
+{
+ PyObject *driver_vars=NULL;
+ PyObject *retval= NULL;
+ PyGILState_STATE gilstate;
+
+ DriverTarget *dtar;
+ float result = 0.0f; /* default return */
+ char *expr = NULL;
+ short targets_ok= 1;
+
+ /* sanity checks - should driver be executed? */
+ if ((driver == NULL) /*|| (G.f & G_DOSCRIPTLINKS)==0*/)
+ return result;
+
+ /* get the py expression to be evaluated */
+ expr = driver->expression;
+ if ((expr == NULL) || (expr[0]=='\0'))
+ return result;
+
+ gilstate = PyGILState_Ensure();
+
+ /* init global dictionary for py-driver evaluation settings */
+ if (!bpy_pydriver_Dict) {
+ if (bpy_pydriver_create_dict() != 0) {
+ fprintf(stderr, "Pydriver error: couldn't create Python dictionary");
+ PyGILState_Release(gilstate);
+ return result;
+ }
+ }
+
+ /* add target values to a dict that will be used as '__locals__' dict */
+ driver_vars = PyDict_New(); // XXX do we need to decref this?
+ for (dtar= driver->targets.first; dtar; dtar= dtar->next) {
+ PyObject *driver_arg = NULL;
+ float tval = 0.0f;
+
+ /* try to get variable value */
+ tval= driver_get_target_value(driver, dtar);
+ driver_arg= PyFloat_FromDouble((double)tval);
+
+ /* try to add to dictionary */
+ if (PyDict_SetItemString(driver_vars, dtar->name, driver_arg)) {
+ /* this target failed - bad name */
+ if (targets_ok) {
+ /* first one - print some extra info for easier identification */
+ fprintf(stderr, "\nBPY_pydriver_eval() - Error while evaluating PyDriver:\n");
+ targets_ok= 0;
+ }
+
+ fprintf(stderr, "\tBPY_pydriver_eval() - couldn't add variable '%s' to namespace \n", dtar->name);
+ // BPy_errors_to_report(NULL); // TODO - reports
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ }
+
+#if 0 // slow
+ /* execute expression to get a value */
+ retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars);
+#else
+ if(driver->flag & DRIVER_FLAG_RECOMPILE || driver->expr_comp==NULL) {
+ Py_XDECREF(driver->expr_comp);
+ driver->expr_comp= Py_CompileString(expr, "<bpy driver>", Py_eval_input);
+ driver->flag &= ~DRIVER_FLAG_RECOMPILE;
+ }
+ if(driver->expr_comp)
+ retval= PyEval_EvalCode(driver->expr_comp, bpy_pydriver_Dict, driver_vars);
+#endif
+
+ /* decref the driver vars first... */
+ Py_DECREF(driver_vars);
+
+ /* process the result */
+ if (retval == NULL) {
+ result = pydriver_error(driver);
+ PyGILState_Release(gilstate);
+ return result;
+ }
+
+ result = (float)PyFloat_AsDouble(retval);
+ Py_DECREF(retval);
+
+ if ((result == -1) && PyErr_Occurred()) {
+ result = pydriver_error(driver);
+ PyGILState_Release(gilstate);
+ return result;
+ }
+
+ /* all fine, make sure the "invalid expression" flag is cleared */
+ driver->flag &= ~DRIVER_FLAG_INVALID;
+
+ PyGILState_Release(gilstate);
+
+ return result;
+}
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index eb69e92ea05..55ba1138b8e 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -49,19 +49,17 @@
#include "BLI_winstuff.h"
#endif
-#include "DNA_anim_types.h"
#include "DNA_space_types.h"
#include "DNA_text_types.h"
#include "MEM_guardedalloc.h"
-#include "BLI_util.h"
+#include "BLI_path_util.h"
#include "BLI_storage.h"
#include "BLI_fileops.h"
#include "BLI_string.h"
#include "BKE_context.h"
-#include "BKE_fcurve.h"
#include "BKE_text.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -77,7 +75,6 @@
#include "../generic/BGL.h"
#include "../generic/IDProp.h"
-
/* for internal use, when starting and ending python scripts */
/* incase a python script triggers another python call, stop bpy_context_clear from invalidating */
@@ -280,6 +277,14 @@ void BPY_start_python_path(void)
/* 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
BLI_setenv("PYTHONHOME", py_path_bundle);
BLI_setenv("PYTHONPATH", py_path_bundle);
@@ -316,11 +321,25 @@ void BPY_start_python( int argc, char **argv )
/* sigh, why do python guys not have a char** version anymore? :( */
{
int i;
+#if 0
PyObject *py_argv= PyList_New(argc);
-
for (i=0; i<argc; i++)
PyList_SET_ITEM(py_argv, i, PyUnicode_FromString(argv[i]));
+#else // should fix bug #20021 - utf path name problems
+ PyObject *py_argv= PyList_New(0);
+ for (i=0; i<argc; i++) {
+ PyObject *item= PyUnicode_Decode(argv[i], strlen(argv[i]), Py_FileSystemDefaultEncoding, NULL);
+ if(item==NULL) { // should never happen
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ else {
+ PyList_Append(py_argv, item);
+ Py_DECREF(item);
+ }
+ }
+#endif
PySys_SetObject("argv", py_argv);
Py_DECREF(py_argv);
}
@@ -538,7 +557,9 @@ int BPY_run_script_space_listener(bContext *C, SpaceScript * sc)
void BPY_DECREF(void *pyob_ptr)
{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
Py_DECREF((PyObject *)pyob_ptr);
+ PyGILState_Release(gilstate);
}
#if 0
@@ -596,201 +617,6 @@ int BPY_run_python_script_space(const char *modulename, const char *func)
#include "PIL_time.h"
#endif
-/* ****************************************** */
-/* Drivers - PyExpression Evaluation */
-
-/* for pydrivers (drivers using one-line Python expressions to express relationships between targets) */
-PyObject *bpy_pydriver_Dict = NULL;
-
-/* For faster execution we keep a special dictionary for pydrivers, with
- * the needed modules and aliases.
- */
-static int bpy_pydriver_create_dict(void)
-{
- PyObject *d, *mod;
-
- /* validate namespace for driver evaluation */
- if (bpy_pydriver_Dict) return -1;
-
- d = PyDict_New();
- if (d == NULL)
- return -1;
- else
- bpy_pydriver_Dict = d;
-
- /* import some modules: builtins, bpy, math, (Blender.noise )*/
- PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins());
-
- mod = PyImport_ImportModule("math");
- if (mod) {
- PyDict_Merge(d, PyModule_GetDict(mod), 0); /* 0 - dont overwrite existing values */
-
- /* Only keep for backwards compat! - just import all math into root, they are standard */
- PyDict_SetItemString(d, "math", mod);
- PyDict_SetItemString(d, "m", mod);
- Py_DECREF(mod);
- }
-
- /* add bpy to global namespace */
- mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
- if (mod) {
- PyDict_SetItemString(bpy_pydriver_Dict, "bpy", mod);
- Py_DECREF(mod);
- }
-
-
-#if 0 // non existant yet
- mod = PyImport_ImportModule("Blender.Noise");
- if (mod) {
- PyDict_SetItemString(d, "noise", mod);
- PyDict_SetItemString(d, "n", mod);
- Py_DECREF(mod);
- } else {
- PyErr_Clear();
- }
-
- /* If there's a Blender text called pydrivers.py, import it.
- * Users can add their own functions to this module.
- */
- if (G.f & G_DOSCRIPTLINKS) {
- mod = importText("pydrivers"); /* can also use PyImport_Import() */
- if (mod) {
- PyDict_SetItemString(d, "pydrivers", mod);
- PyDict_SetItemString(d, "p", mod);
- Py_DECREF(mod);
- } else {
- PyErr_Clear();
- }
- }
-#endif // non existant yet
-
- return 0;
-}
-
-/* Update function, it gets rid of pydrivers global dictionary, forcing
- * BPY_pydriver_eval to recreate it. This function is used to force
- * reloading the Blender text module "pydrivers.py", if available, so
- * updates in it reach pydriver evaluation.
- */
-void BPY_pydriver_update(void)
-{
- PyGILState_STATE gilstate = PyGILState_Ensure();
-
- if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
- PyDict_Clear(bpy_pydriver_Dict);
- Py_DECREF(bpy_pydriver_Dict);
- bpy_pydriver_Dict = NULL;
- }
-
- PyGILState_Release(gilstate);
-
- return;
-}
-
-/* error return function for BPY_eval_pydriver */
-static float pydriver_error(ChannelDriver *driver)
-{
- if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */
- PyDict_Clear(bpy_pydriver_Dict);
- Py_DECREF(bpy_pydriver_Dict);
- bpy_pydriver_Dict = NULL;
- }
-
- driver->flag |= DRIVER_FLAG_INVALID; /* py expression failed */
- fprintf(stderr, "\nError in Driver: The following Python expression failed:\n\t'%s'\n\n", driver->expression);
-
- BPy_errors_to_report(NULL); // TODO - reports
-
- return 0.0f;
-}
-
-/* This evals py driver expressions, 'expr' is a Python expression that
- * should evaluate to a float number, which is returned.
- */
-float BPY_pydriver_eval (ChannelDriver *driver)
-{
- PyObject *driver_vars=NULL;
- PyObject *retval;
- PyGILState_STATE gilstate;
-
- DriverTarget *dtar;
- float result = 0.0f; /* default return */
- char *expr = NULL;
- short targets_ok= 1;
-
- /* sanity checks - should driver be executed? */
- if ((driver == NULL) /*|| (G.f & G_DOSCRIPTLINKS)==0*/)
- return result;
-
- /* get the py expression to be evaluated */
- expr = driver->expression;
- if ((expr == NULL) || (expr[0]=='\0'))
- return result;
-
- gilstate = PyGILState_Ensure();
-
- /* init global dictionary for py-driver evaluation settings */
- if (!bpy_pydriver_Dict) {
- if (bpy_pydriver_create_dict() != 0) {
- fprintf(stderr, "Pydriver error: couldn't create Python dictionary");
- PyGILState_Release(gilstate);
- return result;
- }
- }
-
- /* add target values to a dict that will be used as '__locals__' dict */
- driver_vars = PyDict_New(); // XXX do we need to decref this?
- for (dtar= driver->targets.first; dtar; dtar= dtar->next) {
- PyObject *driver_arg = NULL;
- float tval = 0.0f;
-
- /* try to get variable value */
- tval= driver_get_target_value(driver, dtar);
- driver_arg= PyFloat_FromDouble((double)tval);
-
- /* try to add to dictionary */
- if (PyDict_SetItemString(driver_vars, dtar->name, driver_arg)) {
- /* this target failed - bad name */
- if (targets_ok) {
- /* first one - print some extra info for easier identification */
- fprintf(stderr, "\nBPY_pydriver_eval() - Error while evaluating PyDriver:\n");
- targets_ok= 0;
- }
-
- fprintf(stderr, "\tBPY_pydriver_eval() - couldn't add variable '%s' to namespace \n", dtar->name);
- BPy_errors_to_report(NULL); // TODO - reports
- }
- }
-
- /* execute expression to get a value */
- retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars);
-
- /* decref the driver vars first... */
- Py_DECREF(driver_vars);
-
- /* process the result */
- if (retval == NULL) {
- result = pydriver_error(driver);
- PyGILState_Release(gilstate);
- return result;
- }
-
- result = (float)PyFloat_AsDouble(retval);
- Py_DECREF(retval);
-
- if ((result == -1) && PyErr_Occurred()) {
- result = pydriver_error(driver);
- PyGILState_Release(gilstate);
- return result;
- }
-
- /* all fine, make sure the "invalid expression" flag is cleared */
- driver->flag &= ~DRIVER_FLAG_INVALID;
-
- PyGILState_Release(gilstate);
-
- return result;
-}
int BPY_button_eval(bContext *C, char *expr, double *value)
{
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index f89fc3d4d0c..f6199fffbc7 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -60,10 +60,10 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
if (!PyArg_ParseTuple(args, "sO|O!i:_bpy.ops.call", &opname, &context_dict, &PyDict_Type, &kw, &context))
return NULL;
- ot= WM_operatortype_find(opname, TRUE);
+ ot= WM_operatortype_exists(opname);
if (ot == NULL) {
- PyErr_Format( PyExc_SystemError, "_bpy.ops.call: operator \"%s\"could not be found", opname);
+ PyErr_Format( PyExc_SystemError, "_bpy.ops.call: operator \"%s\" could not be found", opname);
return NULL;
}
@@ -76,7 +76,7 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
Py_XINCREF(context_dict); /* so we done loose it */
if(WM_operator_poll((bContext*)C, ot) == FALSE) {
- PyErr_SetString( PyExc_SystemError, "_bpy.ops.call: operator poll() function failed, context is incorrect");
+ PyErr_Format( PyExc_SystemError, "_bpy.ops.call: operator %.200s.poll() function failed, context is incorrect", opname);
error_val= -1;
}
else {
@@ -230,7 +230,9 @@ static PyObject *pyop_getrna(PyObject *self, PyObject *value)
//RNA_pointer_create(NULL, &RNA_Struct, ot->srna, &ptr);
/* XXX - should call WM_operator_properties_free */
- WM_operator_properties_create(&ptr, ot->idname);
+ WM_operator_properties_create_ptr(&ptr, ot);
+
+
pyrna= (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ptr);
pyrna->freeptr= TRUE;
return (PyObject *)pyrna;
@@ -242,7 +244,9 @@ PyObject *BPY_operator_module( void )
static PyMethodDef pyop_as_string_meth ={"as_string", (PyCFunction) pyop_as_string, METH_VARARGS, NULL};
static PyMethodDef pyop_dir_meth = {"dir", (PyCFunction) pyop_dir, METH_NOARGS, NULL};
static PyMethodDef pyop_getrna_meth = {"get_rna", (PyCFunction) pyop_getrna, METH_O, NULL};
- static PyMethodDef pyop_add_meth = {"add", (PyCFunction) PYOP_wrap_add, METH_O, NULL};
+// static PyMethodDef pyop_add_meth = {"add", (PyCFunction) PYOP_wrap_add, METH_O, NULL};
+ static PyMethodDef pyop_add_macro_meth ={"add_macro", (PyCFunction) PYOP_wrap_add_macro, METH_O, NULL};
+ static PyMethodDef pyop_macro_def_meth ={"macro_define", (PyCFunction) PYOP_wrap_macro_define, METH_VARARGS, NULL};
static PyMethodDef pyop_remove_meth = {"remove", (PyCFunction) PYOP_wrap_remove, METH_O, NULL};
PyObject *submodule = PyModule_New("_bpy.ops");
@@ -252,7 +256,9 @@ PyObject *BPY_operator_module( void )
PyModule_AddObject( submodule, "as_string",PyCFunction_New(&pyop_as_string_meth,NULL) );
PyModule_AddObject( submodule, "dir", PyCFunction_New(&pyop_dir_meth, NULL) );
PyModule_AddObject( submodule, "get_rna", PyCFunction_New(&pyop_getrna_meth, NULL) );
- PyModule_AddObject( submodule, "add", PyCFunction_New(&pyop_add_meth, NULL) );
+// PyModule_AddObject( submodule, "add", PyCFunction_New(&pyop_add_meth, NULL) );
+ PyModule_AddObject( submodule, "add_macro", PyCFunction_New(&pyop_add_macro_meth, NULL) );
+ PyModule_AddObject( submodule, "macro_define",PyCFunction_New(&pyop_macro_def_meth, NULL) );
PyModule_AddObject( submodule, "remove", PyCFunction_New(&pyop_remove_meth, NULL) );
return submodule;
diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c
index 3789c5b1258..774e6317e66 100644
--- a/source/blender/python/intern/bpy_operator_wrap.c
+++ b/source/blender/python/intern/bpy_operator_wrap.c
@@ -1,4 +1,3 @@
-
/**
* $Id$
*
@@ -32,6 +31,7 @@
#include "MEM_guardedalloc.h"
#include "WM_api.h"
#include "WM_types.h"
+#include "UI_interface.h"
#include "ED_screen.h"
#include "RNA_define.h"
@@ -78,10 +78,11 @@ static struct BPY_flag_def pyop_ret_flags[] = {
#define PYOP_EXEC 1
#define PYOP_INVOKE 2
#define PYOP_POLL 3
+#define PYOP_DRAW 4
extern void BPY_update_modules( void ); //XXX temp solution
-static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperator *op, wmEvent *event)
+static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperator *op, wmEvent *event, uiLayout *layout)
{
PyObject *py_class = ot->pyop_data;
PyObject *args;
@@ -89,7 +90,6 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
PointerRNA ptr_context;
PointerRNA ptr_operator;
- PointerRNA ptr_event;
PyGILState_STATE gilstate;
@@ -104,11 +104,16 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
py_class_instance = PyObject_Call(py_class, args, NULL);
Py_DECREF(args);
-
- if (py_class_instance) { /* Initializing the class worked, now run its invoke function */
+
+ if (py_class_instance==NULL) { /* Initializing the class worked, now run its invoke function */
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ else {
RNA_pointer_create(NULL, &RNA_Context, C, &ptr_context);
if (mode==PYOP_INVOKE) {
+ PointerRNA ptr_event;
item= PyObject_GetAttrString(py_class, "invoke");
args = PyTuple_New(3);
@@ -122,7 +127,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
else if (mode==PYOP_EXEC) {
item= PyObject_GetAttrString(py_class, "execute");
args = PyTuple_New(2);
-
+
PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context));
}
else if (mode==PYOP_POLL) {
@@ -130,6 +135,36 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
args = PyTuple_New(2);
PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context));
}
+ else if (mode==PYOP_DRAW) {
+ PointerRNA ptr_layout;
+ item= PyObject_GetAttrString(py_class, "draw");
+ args = PyTuple_New(2);
+
+ RNA_pointer_create(NULL, &RNA_UILayout, layout, &ptr_layout);
+
+ // PyTuple_SET_ITEM "steals" object reference, it is
+ // an object passed shouldn't be DECREF'ed
+ PyTuple_SET_ITEM(args, 1, pyrna_struct_CreatePyObject(&ptr_context));
+#if 0
+ PyTuple_SET_ITEM(args, 2, pyrna_struct_CreatePyObject(&ptr_layout));
+#else
+ {
+ /* mimic panels */
+ PyObject *py_layout= pyrna_struct_CreatePyObject(&ptr_layout);
+ PyObject *pyname= PyUnicode_FromString("layout");
+
+ if(PyObject_GenericSetAttr(py_class_instance, pyname, py_layout)) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+ else {
+ Py_DECREF(py_layout);
+ }
+
+ Py_DECREF(pyname);
+ }
+#endif
+ }
PyTuple_SET_ITEM(args, 0, py_class_instance);
ret = PyObject_Call(item, args, NULL);
@@ -137,11 +172,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
Py_DECREF(args);
Py_DECREF(item);
}
- else {
- PyErr_Print();
- PyErr_Clear();
- }
-
+
if (ret == NULL) { /* covers py_class_instance failing too */
if(op)
BPy_errors_to_report(op->reports);
@@ -149,21 +180,18 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
else {
if (mode==PYOP_POLL) {
if (PyBool_Check(ret) == 0) {
- PyErr_SetString(PyExc_ValueError, "Python poll function return value ");
- if(op)
- BPy_errors_to_report(op->reports);
+ PyErr_Format(PyExc_ValueError, "Python operator '%s.poll', did not return a bool value", ot->idname);
+ BPy_errors_to_report(op ? op->reports:NULL); /* prints and clears if NULL given */
}
else {
ret_flag= ret==Py_True ? 1:0;
}
-
+ } else if(mode==PYOP_DRAW) {
+ /* pass */
} else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
/* the returned value could not be converted into a flag */
- if(op) {
- fprintf(stderr, "error using return value from \"%s\"\n", op->idname); // for some reason the error raised doesnt include file:line... this helps
- BPy_errors_to_report(op->reports);
- }
-
+ PyErr_Format(PyExc_ValueError, "Python operator, error using return value from \"%s\"\n", ot->idname);
+ BPy_errors_to_report(op ? op->reports:NULL);
ret_flag = OPERATOR_CANCELLED;
}
/* there is no need to copy the py keyword dict modified by
@@ -173,7 +201,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
* If we ever want to do this and use the props again,
* it can be done with - pyrna_pydict_to_props(op->ptr, kw, "")
*/
-
+
Py_DECREF(ret);
}
@@ -186,7 +214,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
BPY_flag_def *flag_def = pyop_ret_flags;
strcpy(flag_str, "");
-
+
while(flag_def->name) {
if (ret_flag & flag_def->flag) {
if(flag_str[1])
@@ -213,19 +241,67 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperatorType *ot, wmOperat
static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
- return PYTHON_OT_generic(PYOP_INVOKE, C, op->type, op, event);
+ return PYTHON_OT_generic(PYOP_INVOKE, C, op->type, op, event, NULL);
}
static int PYTHON_OT_execute(bContext *C, wmOperator *op)
{
- return PYTHON_OT_generic(PYOP_EXEC, C, op->type, op, NULL);
+ return PYTHON_OT_generic(PYOP_EXEC, C, op->type, op, NULL, NULL);
}
static int PYTHON_OT_poll(bContext *C, wmOperatorType *ot)
{
- return PYTHON_OT_generic(PYOP_POLL, C, ot, NULL, NULL);
+ return PYTHON_OT_generic(PYOP_POLL, C, ot, NULL, NULL, NULL);
}
+static void PYTHON_OT_draw(bContext *C, wmOperator *op, uiLayout *layout)
+{
+ PYTHON_OT_generic(PYOP_DRAW, C, op->type, op, NULL, layout);
+}
+
+
+
+void operator_wrapper(wmOperatorType *ot, void *userdata)
+{
+ /* take care not to overwrite anything set in
+ * WM_operatortype_append_ptr before opfunc() is called */
+ StructRNA *srna = ot->srna;
+ *ot= *((wmOperatorType *)userdata);
+ ot->srna= srna; /* restore */
+
+ RNA_struct_blender_type_set(ot->ext.srna, ot);
+
+
+ /* Can't use this because it returns a dict proxy
+ *
+ * item= PyObject_GetAttrString(py_class, "__dict__");
+ */
+ {
+ PyObject *py_class = ot->ext.data;
+ PyObject *item= ((PyTypeObject*)py_class)->tp_dict;
+ if(item) {
+ /* only call this so pyrna_deferred_register_props gives a useful error
+ * WM_operatortype_append_ptr will call RNA_def_struct_identifier
+ * later */
+ RNA_def_struct_identifier(ot->srna, ot->idname);
+
+ if(pyrna_deferred_register_props(ot->srna, item)!=0) {
+ /* failed to register operator props */
+ PyErr_Print();
+ PyErr_Clear();
+
+ }
+ }
+ else {
+ PyErr_Clear();
+ }
+ }
+}
+
+
+
+
+
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
{
PyObject *py_class = (PyObject *)userdata;
@@ -249,8 +325,8 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
item= PyObject_GetAttrString(py_class, PYOP_ATTR_DESCRIPTION);
ot->description= (item && PyUnicode_Check(item)) ? _PyUnicode_AsString(item):"undocumented python operator";
Py_XDECREF(item);
-
- /* api callbacks, detailed checks dont on adding */
+
+ /* api callbacks, detailed checks dont on adding */
if (PyObject_HasAttrString(py_class, "invoke"))
ot->invoke= PYTHON_OT_invoke;
//else
@@ -260,9 +336,11 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
ot->exec= PYTHON_OT_execute;
if (PyObject_HasAttrString(py_class, "poll"))
ot->pyop_poll= PYTHON_OT_poll;
-
+ if (PyObject_HasAttrString(py_class, "draw"))
+ ot->ui= PYTHON_OT_draw;
+
ot->pyop_data= userdata;
-
+
/* flags */
ot->flag= 0;
@@ -306,14 +384,88 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
}
}
+void PYTHON_OT_MACRO_wrapper(wmOperatorType *ot, void *userdata)
+{
+ PyObject *py_class = (PyObject *)userdata;
+ PyObject *item;
+
+ /* identifiers */
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME_BL);
+ ot->idname= _PyUnicode_AsString(item);
+ Py_DECREF(item);
+
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_UINAME);
+ if (item) {
+ ot->name= _PyUnicode_AsString(item);
+ Py_DECREF(item);
+ }
+ else {
+ ot->name= ot->idname;
+ PyErr_Clear();
+ }
+
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_DESCRIPTION);
+ ot->description= (item && PyUnicode_Check(item)) ? _PyUnicode_AsString(item):"undocumented python operator";
+ Py_XDECREF(item);
+
+ if (PyObject_HasAttrString(py_class, "poll"))
+ ot->pyop_poll= PYTHON_OT_poll;
+ if (PyObject_HasAttrString(py_class, "draw"))
+ ot->ui= PYTHON_OT_draw;
+
+ ot->pyop_data= userdata;
+
+ /* flags */
+ ot->flag= OPTYPE_MACRO; /* macro at least */
+
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_REGISTER);
+ if (item) {
+ ot->flag |= PyObject_IsTrue(item)!=0 ? OPTYPE_REGISTER:0;
+ Py_DECREF(item);
+ }
+ else {
+ PyErr_Clear();
+ }
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_UNDO);
+ if (item) {
+ ot->flag |= PyObject_IsTrue(item)!=0 ? OPTYPE_UNDO:0;
+ Py_DECREF(item);
+ }
+ else {
+ PyErr_Clear();
+ }
+
+ /* Can't use this because it returns a dict proxy
+ *
+ * item= PyObject_GetAttrString(py_class, "__dict__");
+ */
+ item= ((PyTypeObject*)py_class)->tp_dict;
+ if(item) {
+ /* only call this so pyrna_deferred_register_props gives a useful error
+ * WM_operatortype_append_macro_ptr will call RNA_def_struct_identifier
+ * later */
+ RNA_def_struct_identifier(ot->srna, ot->idname);
+
+ if(pyrna_deferred_register_props(ot->srna, item)!=0) {
+ /* failed to register operator props */
+ PyErr_Print();
+ PyErr_Clear();
+
+ }
+ }
+ else {
+ PyErr_Clear();
+ }
+}
+
/* pyOperators - Operators defined IN Python */
PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
-{
+{
PyObject *base_class, *item;
wmOperatorType *ot;
-
-
+
+
char *idname= NULL;
char idname_bl[OP_MAX_TYPENAME]; /* converted to blender syntax */
@@ -324,6 +476,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
{"execute", 'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
{"invoke", 'f', 3, -1, BPY_CLASS_ATTR_OPTIONAL},
{"poll", 'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
+ {"draw", 'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
{NULL, 0, 0, 0}
};
@@ -355,7 +508,7 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
Py_DECREF(item);
/* end annoying conversion! */
-
+
/* remove if it already exists */
if ((ot=WM_operatortype_exists(idname))) {
if(ot->pyop_data) {
@@ -363,13 +516,123 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
}
WM_operatortype_remove(idname);
}
-
+
Py_INCREF(py_class);
WM_operatortype_append_ptr(PYTHON_OT_wrapper, py_class);
Py_RETURN_NONE;
}
+/* pyOperators - Macro Operators defined IN Python */
+PyObject *PYOP_wrap_add_macro(PyObject *self, PyObject *py_class)
+{
+ PyObject *base_class, *item;
+ wmOperatorType *ot;
+
+
+ char *idname= NULL;
+ char idname_bl[OP_MAX_TYPENAME]; /* converted to blender syntax */
+
+ static struct BPY_class_attr_check pyop_class_attr_values[]= {
+ {PYOP_ATTR_IDNAME, 's', -1, OP_MAX_TYPENAME-3, 0}, /* -3 because a.b -> A_OT_b */
+ {PYOP_ATTR_UINAME, 's', -1,-1, BPY_CLASS_ATTR_OPTIONAL},
+ {PYOP_ATTR_DESCRIPTION, 's', -1,-1, BPY_CLASS_ATTR_NONE_OK},
+ {"poll", 'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
+ {"draw", 'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
+ {NULL, 0, 0, 0}
+ };
+
+ //PyObject bpy_mod= PyDict_GetItemString(PyEval_GetGlobals(), "bpy");
+ PyObject *bpy_mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
+ base_class = PyObject_GetAttrStringArgs(bpy_mod, 2, "types", "Macro");
+ Py_DECREF(bpy_mod);
+
+ if(BPY_class_validate("Macro", py_class, base_class, pyop_class_attr_values, NULL) < 0) {
+ return NULL; /* BPY_class_validate sets the error */
+ }
+ Py_DECREF(base_class);
+
+ /* class name is used for operator ID - this can be changed later if we want */
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
+ idname = _PyUnicode_AsString(item);
+
+
+ /* annoying conversion! */
+ WM_operator_bl_idname(idname_bl, idname);
+ Py_DECREF(item);
+
+ item= PyUnicode_FromString(idname_bl);
+ PyObject_SetAttrString(py_class, PYOP_ATTR_IDNAME_BL, item);
+ idname = _PyUnicode_AsString(item);
+ Py_DECREF(item);
+ /* end annoying conversion! */
+
+
+ /* remove if it already exists */
+ if ((ot=WM_operatortype_exists(idname))) {
+ if(ot->pyop_data) {
+ Py_XDECREF((PyObject*)ot->pyop_data);
+ }
+ WM_operatortype_remove(idname);
+ }
+
+ Py_INCREF(py_class);
+ WM_operatortype_append_macro_ptr(PYTHON_OT_MACRO_wrapper, py_class);
+
+ Py_RETURN_NONE;
+}
+
+PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args)
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+ PyObject *macro;
+ PyObject *item;
+ PointerRNA ptr_otmacro;
+
+ char *opname;
+ char *macroname;
+
+ if (!PyArg_ParseTuple(args, "Os:_bpy.ops.macro_define", &macro, &opname))
+ return NULL;
+
+ if (WM_operatortype_exists(opname) == NULL) {
+ PyErr_Format(PyExc_ValueError, "Macro Define: '%s' is not a valid operator id", opname);
+ return NULL;
+ }
+
+ /* identifiers */
+ item= PyObject_GetAttrString(macro, PYOP_ATTR_IDNAME_BL);
+
+ if (!item) {
+ item= PyObject_GetAttrString(macro, PYOP_ATTR_IDNAME);
+
+ if (!item) {
+ PyErr_Format(PyExc_ValueError, "Macro Define: not a valid Macro class");
+ } else {
+ macroname= _PyUnicode_AsString(item);
+ PyErr_Format(PyExc_ValueError, "Macro Define: '%s' hasn't been registered yet", macroname);
+ }
+ return NULL;
+ }
+
+ macroname= _PyUnicode_AsString(item);
+
+ ot = WM_operatortype_exists(macroname);
+
+ if (!ot) {
+ PyErr_Format(PyExc_ValueError, "Macro Define: '%s' is not a valid macro or hasn't been registered yet", macroname);
+ return NULL;
+ }
+
+ otmacro = WM_operatortype_macro_define(ot, opname);
+
+ RNA_pointer_create(NULL, &RNA_OperatorTypeMacro, otmacro, &ptr_otmacro);
+
+ return pyrna_struct_CreatePyObject(&ptr_otmacro);
+}
+
+
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
{
PyObject *py_class;
@@ -406,6 +669,3 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
Py_RETURN_NONE;
}
-
-
-
diff --git a/source/blender/python/intern/bpy_operator_wrap.h b/source/blender/python/intern/bpy_operator_wrap.h
index 9718e2d6e65..c9398551af0 100644
--- a/source/blender/python/intern/bpy_operator_wrap.h
+++ b/source/blender/python/intern/bpy_operator_wrap.h
@@ -29,6 +29,8 @@
/* these are used for operator methods, used by bpy_operator.c */
PyObject *PYOP_wrap_add(PyObject *self, PyObject *args);
+PyObject *PYOP_wrap_add_macro(PyObject *self, PyObject *args);
+PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args);
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args);
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index cdf4f518b51..8eda19d8198 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -39,6 +39,9 @@
#include "BKE_global.h" /* evil G.* */
#include "BKE_report.h"
+#include "BKE_animsys.h"
+#include "BKE_fcurve.h"
+
/* only for keyframing */
#include "DNA_scene_types.h"
#include "DNA_anim_types.h"
@@ -140,18 +143,20 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
PyObject *ret= NULL;
#ifdef USE_MATHUTILS
- int type, subtype, totdim;
+ int subtype, totdim;
int len;
+ /* disallow dynamic sized arrays to be wrapped since the size could change
+ * to a size mathutils does not support */
+ if ((RNA_property_type(prop) != PROP_FLOAT) || (RNA_property_flag(prop) & PROP_DYNAMIC))
+ return NULL;
+
len= RNA_property_array_length(ptr, prop);
- type= RNA_property_type(prop);
subtype= RNA_property_subtype(prop);
totdim= RNA_property_array_dimension(ptr, prop, NULL);
- if (type != PROP_FLOAT) return NULL;
-
if (totdim == 1 || (totdim == 2 && subtype == PROP_MATRIX)) {
- ret = pyrna_prop_CreatePyObject(ptr, prop);
+ ret = pyrna_prop_CreatePyObject(ptr, prop); /* owned by the Mathutils PyObject */
switch(RNA_property_subtype(prop)) {
case PROP_TRANSLATION:
@@ -159,6 +164,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
case PROP_VELOCITY:
case PROP_ACCELERATION:
case PROP_XYZ:
+ case PROP_XYZ|PROP_UNIT_LENGTH:
if(len>=2 && len <= 4) {
PyObject *vec_cb= newVectorObject_cb(ret, len, mathutils_rna_array_cb_index, FALSE);
Py_DECREF(ret); /* the vector owns now */
@@ -374,41 +380,30 @@ static int pyrna_string_to_enum(PyObject *item, PointerRNA *ptr, PropertyRNA *pr
return 1;
}
-PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
+static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val)
{
- PyObject *ret;
- int type = RNA_property_type(prop);
+ PyObject *item, *ret= NULL;
- if (RNA_property_array_check(ptr, prop)) {
- return pyrna_py_from_array(ptr, prop);
- }
-
- /* see if we can coorce into a python type - PropertyType */
- switch (type) {
- case PROP_BOOLEAN:
- ret = PyBool_FromLong( RNA_property_boolean_get(ptr, prop) );
- break;
- case PROP_INT:
- ret = PyLong_FromSsize_t( (Py_ssize_t)RNA_property_int_get(ptr, prop) );
- break;
- case PROP_FLOAT:
- ret = PyFloat_FromDouble( RNA_property_float_get(ptr, prop) );
- break;
- case PROP_STRING:
- {
- char *buf;
- buf = RNA_property_string_get_alloc(ptr, prop, NULL, -1);
- ret = PyUnicode_FromString( buf );
- MEM_freeN(buf);
- break;
+ if(RNA_property_flag(prop) & PROP_ENUM_FLAG) {
+ const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1];
+
+ ret= PySet_New(NULL);
+
+ if (RNA_property_enum_bitflag_identifiers(BPy_GetContext(), ptr, prop, val, identifier)) {
+ int index;
+
+ for(index=0; identifier[index]; index++) {
+ item= PyUnicode_FromString(identifier[index]);
+ PySet_Add(ret, item);
+ Py_DECREF(item);
+ }
+
+ }
}
- case PROP_ENUM:
- {
+ else {
const char *identifier;
- int val = RNA_property_enum_get(ptr, prop);
-
if (RNA_property_enum_identifier(BPy_GetContext(), ptr, prop, val, &identifier)) {
- ret = PyUnicode_FromString( identifier );
+ ret = PyUnicode_FromString(identifier);
} else {
EnumPropertyItem *item;
int free= FALSE;
@@ -417,11 +412,11 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
* right values, python code should not generate error for that */
RNA_property_enum_items(BPy_GetContext(), ptr, prop, &item, NULL, &free);
if(item && item->identifier) {
- ret = PyUnicode_FromString( item->identifier );
+ ret= PyUnicode_FromString(item->identifier);
}
else {
- char *ptr_name= RNA_struct_name_get_alloc(ptr, NULL, FALSE);
-
+ char *ptr_name= RNA_struct_name_get_alloc(ptr, NULL, FALSE);
+
/* prefer not fail silently incase of api errors, maybe disable it later */
printf("RNA Warning: Current value \"%d\" matches no enum in '%s', '%s', '%s'\n", val, RNA_struct_identifier(ptr->type), ptr_name, RNA_property_identifier(prop));
@@ -431,8 +426,8 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
PyErr_Warn(PyExc_RuntimeWarning, error_str);
#endif
- if(ptr_name)
- MEM_freeN(ptr_name);
+ if(ptr_name)
+ MEM_freeN(ptr_name);
ret = PyUnicode_FromString( "" );
}
@@ -443,7 +438,42 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
/*PyErr_Format(PyExc_AttributeError, "RNA Error: Current value \"%d\" matches no enum", val);
ret = NULL;*/
}
+ }
+ return ret;
+}
+
+PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
+{
+ PyObject *ret;
+ int type = RNA_property_type(prop);
+
+ if (RNA_property_array_check(ptr, prop)) {
+ return pyrna_py_from_array(ptr, prop);
+ }
+
+ /* see if we can coorce into a python type - PropertyType */
+ switch (type) {
+ case PROP_BOOLEAN:
+ ret = PyBool_FromLong( RNA_property_boolean_get(ptr, prop) );
+ break;
+ case PROP_INT:
+ ret = PyLong_FromSsize_t( (Py_ssize_t)RNA_property_int_get(ptr, prop) );
+ break;
+ case PROP_FLOAT:
+ ret = PyFloat_FromDouble( RNA_property_float_get(ptr, prop) );
+ break;
+ case PROP_STRING:
+ {
+ char *buf;
+ buf = RNA_property_string_get_alloc(ptr, prop, NULL, -1);
+ ret = PyUnicode_FromString( buf );
+ MEM_freeN(buf);
+ break;
+ }
+ case PROP_ENUM:
+ {
+ ret= pyrna_enum_to_py(ptr, prop, RNA_property_enum_get(ptr, prop));
break;
}
case PROP_POINTER:
@@ -529,7 +559,7 @@ int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, int all_args, const cha
static PyObject * pyrna_func_call(PyObject *self, PyObject *args, PyObject *kw);
-PyObject *pyrna_func_to_py(BPy_DummyPointerRNA *pyrna, FunctionRNA *func)
+static PyObject *pyrna_func_to_py(BPy_DummyPointerRNA *pyrna, FunctionRNA *func)
{
static PyMethodDef func_meth = {"<generic rna function>", (PyCFunction)pyrna_func_call, METH_VARARGS|METH_KEYWORDS, "python rna function"};
PyObject *self;
@@ -554,6 +584,7 @@ PyObject *pyrna_func_to_py(BPy_DummyPointerRNA *pyrna, FunctionRNA *func)
}
+
int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *value, const char *error_prefix)
{
/* XXX hard limits should be checked here */
@@ -648,28 +679,36 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v
}
case PROP_ENUM:
{
- int val, i;
+ int val= 0, tmpval;
if (PyUnicode_Check(value)) {
if (!pyrna_string_to_enum(value, ptr, prop, &val, error_prefix))
return -1;
}
- else if (PyTuple_Check(value)) {
- /* tuple of enum items, concatenate all values with OR */
- val= 0;
- for (i= 0; i < PyTuple_Size(value); i++) {
- int tmpval;
-
- /* PyTuple_GET_ITEM returns a borrowed reference */
- if (!pyrna_string_to_enum(PyTuple_GET_ITEM(value, i), ptr, prop, &tmpval, error_prefix))
- return -1;
+ else if (PyAnySet_Check(value)) {
+ if(RNA_property_flag(prop) & PROP_ENUM_FLAG) {
+ /* set of enum items, concatenate all values with OR */
+
+ /* set looping */
+ Py_ssize_t pos = 0;
+ PyObject *key;
+ long hash;
- val |= tmpval;
+ while (_PySet_NextEntry(value, &pos, &key, &hash)) {
+ if (!pyrna_string_to_enum(key, ptr, prop, &tmpval, error_prefix))
+ return -1;
+
+ val |= tmpval;
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError, "%.200s, %.200s.%.200s is not a bitflag enum type", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop));
+ return -1;
}
}
else {
char *enum_str= pyrna_enum_as_string(ptr, prop);
- PyErr_Format(PyExc_TypeError, "%.200s expected a string enum or a tuple of strings in (%.200s)", error_prefix, enum_str);
+ PyErr_Format(PyExc_TypeError, "%.200s expected a string enum or a set of strings in (%.200s)", error_prefix, enum_str);
MEM_freeN(enum_str);
return -1;
}
@@ -1129,21 +1168,28 @@ static PyMappingMethods pyrna_prop_as_mapping = {
static int pyrna_prop_contains(BPy_PropertyRNA *self, PyObject *value)
{
PointerRNA newptr; /* not used, just so RNA_property_collection_lookup_string runs */
- char *keyname = _PyUnicode_AsString(value);
- if(keyname==NULL) {
- PyErr_SetString(PyExc_TypeError, "PropertyRNA - key in prop, key must be a string type");
- return -1;
- }
+ if (RNA_property_type(self->prop) == PROP_COLLECTION) {
+ /* key in dict style check */
+ char *keyname = _PyUnicode_AsString(value);
- if (RNA_property_type(self->prop) != PROP_COLLECTION) {
- PyErr_SetString(PyExc_TypeError, "PropertyRNA - key in prop, is only valid for collection types");
- return -1;
- }
+ if(keyname==NULL) {
+ PyErr_SetString(PyExc_TypeError, "PropertyRNA - key in prop, key must be a string type");
+ return -1;
+ }
- if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr))
- return 1;
+ if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr))
+ return 1;
+ }
+ else if (RNA_property_array_check(&self->ptr, self->prop)) {
+ /* value in list style check */
+ return pyrna_array_contains_py(&self->ptr, self->prop, value);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "PropertyRNA - type is not an array or a collection");
+ return -1;
+ }
return 0;
}
@@ -1158,7 +1204,7 @@ static int pyrna_struct_contains(BPy_StructRNA *self, PyObject *value)
return -1;
}
- if(RNA_struct_idproperties_check(&self->ptr)==0) {
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
PyErr_SetString( PyExc_TypeError, "this type doesnt support IDProperties");
return -1;
}
@@ -1212,7 +1258,7 @@ static PyObject *pyrna_struct_subscript( BPy_StructRNA *self, PyObject *key )
IDProperty *group, *idprop;
char *name= _PyUnicode_AsString(key);
- if(RNA_struct_idproperties_check(&self->ptr)==0) {
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
PyErr_SetString( PyExc_TypeError, "this type doesn't support IDProperties");
return NULL;
}
@@ -1261,7 +1307,7 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
{
IDProperty *group;
- if(RNA_struct_idproperties_check(&self->ptr)==0) {
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
PyErr_SetString( PyExc_TypeError, "this type doesnt support IDProperties");
return NULL;
}
@@ -1278,7 +1324,7 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
{
IDProperty *group;
- if(RNA_struct_idproperties_check(&self->ptr)==0) {
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
PyErr_SetString( PyExc_TypeError, "this type doesnt support IDProperties");
return NULL;
}
@@ -1296,7 +1342,7 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
{
IDProperty *group;
- if(RNA_struct_idproperties_check(&self->ptr)==0) {
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
PyErr_SetString( PyExc_TypeError, "this type doesnt support IDProperties");
return NULL;
}
@@ -1356,7 +1402,7 @@ static PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args)
char *path, *path_full;
int index= -1; /* default to all */
PropertyRNA *prop;
- PyObject *result;
+ PyObject *ret;
if (!PyArg_ParseTuple(args, "s|i:driver_add", &path, &index))
return NULL;
@@ -1385,13 +1431,40 @@ static PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args)
return NULL;
}
- result= PyBool_FromLong( ANIM_add_driver((ID *)self->ptr.id.data, path_full, index, 0, DRIVER_TYPE_PYTHON));
+ if(ANIM_add_driver((ID *)self->ptr.id.data, path_full, index, 0, DRIVER_TYPE_PYTHON)) {
+ ID *id= self->ptr.id.data;
+ AnimData *adt= BKE_animdata_from_id(id);
+ FCurve *fcu;
+
+ PointerRNA tptr;
+ PyObject *item;
+
+ if(index == -1) { /* all, use a list */
+ int i= 0;
+ ret= PyList_New(0);
+ while((fcu= list_find_fcurve(&adt->drivers, path_full, i++))) {
+ RNA_pointer_create(id, &RNA_FCurve, fcu, &tptr);
+ item= pyrna_struct_CreatePyObject(&tptr);
+ PyList_Append(ret, item);
+ Py_DECREF(item);
+ }
+ }
+ else {
+ fcu= list_find_fcurve(&adt->drivers, path_full, index);
+ RNA_pointer_create(id, &RNA_FCurve, fcu, &tptr);
+ ret= pyrna_struct_CreatePyObject(&tptr);
+ }
+ }
+ else {
+ ret= Py_None;
+ Py_INCREF(ret);
+ }
+
MEM_freeN(path_full);
- return result;
+ return ret;
}
-
static PyObject *pyrna_struct_is_property_set(BPy_StructRNA *self, PyObject *args)
{
char *name;
@@ -1434,6 +1507,59 @@ static PyObject *pyrna_struct_path_resolve(BPy_StructRNA *self, PyObject *value)
Py_RETURN_NONE;
}
+static PyObject *pyrna_struct_path_to_id(BPy_StructRNA *self, PyObject *args)
+{
+ char *name= NULL;
+ char *path;
+ PropertyRNA *prop;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "|s:path_to_id", &name))
+ return NULL;
+
+ if(name) {
+ prop= RNA_struct_find_property(&self->ptr, name);
+ if(prop==NULL) {
+ PyErr_Format(PyExc_TypeError, "path_to_id(\"%.200s\") not found", name);
+ return NULL;
+ }
+
+ path= RNA_path_from_ID_to_property(&self->ptr, prop);
+ }
+ else {
+ path= RNA_path_from_ID_to_struct(&self->ptr);
+ }
+
+ if(path==NULL) {
+ if(name) PyErr_Format(PyExc_TypeError, "%.200s.path_to_id(\"%s\") found but does not support path creation", RNA_struct_identifier(self->ptr.type), name);
+ else PyErr_Format(PyExc_TypeError, "%.200s.path_to_id() does not support path creation for this type", name);
+ return NULL;
+ }
+
+ ret= PyUnicode_FromString(path);
+ MEM_freeN(path);
+
+ return ret;
+}
+
+static PyObject *pyrna_prop_path_to_id(BPy_PropertyRNA *self)
+{
+ char *path;
+ PropertyRNA *prop = self->prop;
+ PyObject *ret;
+
+ path= RNA_path_from_ID_to_property(&self->ptr, self->prop);
+
+ if(path==NULL) {
+ PyErr_Format(PyExc_TypeError, "%.200s.%.200s.path_to_id() does not support path creation for this type", RNA_struct_identifier(self->ptr.type), RNA_property_identifier(prop));
+ return NULL;
+ }
+
+ ret= PyUnicode_FromString(path);
+ MEM_freeN(path);
+
+ return ret;
+}
static void pyrna_dir_members_py(PyObject *list, PyObject *self)
{
@@ -1532,14 +1658,6 @@ static PyObject *pyrna_struct_dir(BPy_StructRNA *self)
BLI_freelistN(&lb);
}
-
- /* Hard coded names */
- if(self->ptr.id.data) {
- pystring = PyUnicode_FromString("id_data");
- PyList_Append(ret, pystring);
- Py_DECREF(pystring);
- }
-
return ret;
}
@@ -1553,7 +1671,7 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA *self, PyObject *pyname )
if(name[0]=='_') { // rna can't start with a "_", so for __dict__ and similar we can skip using rna lookups
/* annoying exception, maybe we need to have different types for this... */
- if((strcmp(name, "__getitem__")==0 || strcmp(name, "__setitem__")==0) && !RNA_struct_idproperties_check(&self->ptr)) {
+ if((strcmp(name, "__getitem__")==0 || strcmp(name, "__setitem__")==0) && !RNA_struct_idproperties_check(self->ptr.type)) {
PyErr_SetString(PyExc_AttributeError, "StructRNA - no __getitem__ support for this type");
ret = NULL;
}
@@ -1606,17 +1724,6 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA *self, PyObject *pyname )
BLI_freelistN(&newlb);
}
- else if (strcmp(name, "id_data")==0) { /* XXX - hard coded */
- if(self->ptr.id.data) {
- PointerRNA id_ptr;
- RNA_id_pointer_create((ID *)self->ptr.id.data, &id_ptr);
- ret = pyrna_struct_CreatePyObject(&id_ptr);
- }
- else {
- ret = Py_None;
- Py_INCREF(ret);
- }
- }
else {
#if 0
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%.200s\" not found", name);
@@ -1653,19 +1760,22 @@ static int pyrna_struct_setattro( BPy_StructRNA *self, PyObject *pyname, PyObjec
PropertyRNA *prop = RNA_struct_find_property(&self->ptr, name);
if (prop==NULL) {
+ return PyObject_GenericSetAttr((PyObject *)self, pyname, value);
+#if 0
// XXX - This currently allows anything to be assigned to an rna prop, need to see how this should be used
// but for now it makes porting scripts confusing since it fails silently.
// edit: allowing this for setting classes internal attributes.
// edit: allow this for any attribute that alredy exists as a python attr
if ( (name[0]=='_' /* || pyrna_struct_pydict_contains(self, pyname) */ ) &&
!BPy_StructRNA_CheckExact(self) &&
- PyObject_GenericSetAttr((PyObject *)self, pyname, value) >= 0) {
+
return 0;
} else
{
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%.200s\" not found", name);
return -1;
}
+#endif
}
if (!RNA_property_editable(&self->ptr, prop)) {
@@ -1785,6 +1895,17 @@ static PyObject *pyrna_prop_remove(BPy_PropertyRNA *self, PyObject *value)
return ret;
}
+static PyObject *pyrna_struct_get_id_data(BPy_StructRNA *self)
+{
+ if(self->ptr.id.data) {
+ PointerRNA id_ptr;
+ RNA_id_pointer_create((ID *)self->ptr.id.data, &id_ptr);
+ return pyrna_struct_CreatePyObject(&id_ptr);
+ }
+
+ Py_RETURN_NONE;
+}
+
/*****************************************************************************/
/* Python attributes get/set structure: */
/*****************************************************************************/
@@ -1795,6 +1916,11 @@ static PyGetSetDef pyrna_prop_getseters[] = {
};
#endif
+static PyGetSetDef pyrna_struct_getseters[] = {
+ {"id_data", (getter)pyrna_struct_get_id_data, (setter)NULL, "The ID data this datablock is from, (not available for all data)", NULL},
+ {NULL,NULL,NULL,NULL,NULL} /* Sentinel */
+};
+
static PyObject *pyrna_prop_keys(BPy_PropertyRNA *self)
{
PyObject *ret;
@@ -1890,6 +2016,34 @@ static PyObject *pyrna_prop_values(BPy_PropertyRNA *self)
return ret;
}
+static PyObject *pyrna_struct_get(BPy_StructRNA *self, PyObject *args)
+{
+ IDProperty *group, *idprop;
+
+ char *key;
+ PyObject* def = Py_None;
+
+ if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
+ return NULL;
+
+ /* mostly copied from BPy_IDGroup_Map_GetItem */
+ if(RNA_struct_idproperties_check(self->ptr.type)==0) {
+ PyErr_SetString( PyExc_TypeError, "this type doesn't support IDProperties");
+ return NULL;
+ }
+
+ group= RNA_struct_idproperties(&self->ptr, 0);
+ if(group) {
+ idprop= IDP_GetPropertyFromGroup(group, key);
+
+ if(idprop)
+ return BPy_IDGroup_WrapData(self->ptr.id.data, idprop);
+ }
+
+ Py_INCREF(def);
+ return def;
+}
+
static PyObject *pyrna_prop_get(BPy_PropertyRNA *self, PyObject *args)
{
PointerRNA newptr;
@@ -2115,6 +2269,9 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set)
}
}
+ if(array)
+ PyMem_Free(array);
+
if(PyErr_Occurred()) {
/* Maybe we could make our own error */
PyErr_Print();
@@ -2126,9 +2283,6 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set)
return NULL;
}
- if(array)
- PyMem_Free(array);
-
Py_RETURN_NONE;
}
@@ -2182,12 +2336,15 @@ static struct PyMethodDef pyrna_struct_methods[] = {
{"values", (PyCFunction)pyrna_struct_values, METH_NOARGS, NULL},
{"items", (PyCFunction)pyrna_struct_items, METH_NOARGS, NULL},
+ {"get", (PyCFunction)pyrna_struct_get, METH_VARARGS, NULL},
+
/* maybe this become and ID function */
{"keyframe_insert", (PyCFunction)pyrna_struct_keyframe_insert, METH_VARARGS, NULL},
{"driver_add", (PyCFunction)pyrna_struct_driver_add, METH_VARARGS, NULL},
{"is_property_set", (PyCFunction)pyrna_struct_is_property_set, METH_VARARGS, NULL},
{"is_property_hidden", (PyCFunction)pyrna_struct_is_property_hidden, METH_VARARGS, NULL},
{"path_resolve", (PyCFunction)pyrna_struct_path_resolve, METH_O, NULL},
+ {"path_to_id", (PyCFunction)pyrna_struct_path_to_id, METH_VARARGS, NULL},
{"__dir__", (PyCFunction)pyrna_struct_dir, METH_NOARGS, NULL},
{NULL, NULL, 0, NULL}
};
@@ -2203,6 +2360,9 @@ static struct PyMethodDef pyrna_prop_methods[] = {
{"add", (PyCFunction)pyrna_prop_add, METH_NOARGS, NULL},
{"remove", (PyCFunction)pyrna_prop_remove, METH_O, NULL},
+ /* almost the same as the srna function */
+ {"path_to_id", (PyCFunction)pyrna_prop_path_to_id, METH_NOARGS, NULL},
+
/* array accessor function */
{"foreach_get", (PyCFunction)pyrna_prop_foreach_get, METH_VARARGS, NULL},
{"foreach_set", (PyCFunction)pyrna_prop_foreach_set, METH_VARARGS, NULL},
@@ -2300,22 +2460,7 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data)
}
case PROP_ENUM:
{
- const char *identifier;
- int val = *(int*)data;
-
- if (RNA_property_enum_identifier(BPy_GetContext(), ptr, prop, val, &identifier)) {
- ret = PyUnicode_FromString( identifier );
- } else {
- /* prefer not fail silently incase of api errors, maybe disable it later */
- char error_str[128];
- sprintf(error_str, "RNA Warning: Current value \"%d\" matches no enum", val);
- PyErr_Warn(PyExc_RuntimeWarning, error_str);
-
- ret = PyUnicode_FromString( "" );
- /*PyErr_Format(PyExc_AttributeError, "RNA Error: Current value \"%d\" matches no enum", val);
- ret = NULL;*/
- }
-
+ ret= pyrna_enum_to_py(ptr, prop, *(int*)data);
break;
}
case PROP_POINTER:
@@ -2523,6 +2668,9 @@ static PyObject * pyrna_func_call(PyObject *self, PyObject *args, PyObject *kw)
RNA_parameter_list_begin(&parms, &iter);
for(; iter.valid; RNA_parameter_list_next(&iter)) {
parm= iter.parm;
+ if(RNA_property_flag(parm) & PROP_RETURN)
+ continue;
+
BLI_dynstr_appendf(good_args, first ? "%s" : ", %s", RNA_property_identifier(parm));
first= FALSE;
}
@@ -2636,7 +2784,7 @@ PyTypeObject pyrna_struct_Type = {
/*** Attribute descriptor and subclassing stuff ***/
pyrna_struct_methods, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
- NULL, /* struct PyGetSetDef *tp_getset; */
+ pyrna_struct_getseters, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
NULL, /* PyObject *tp_dict; */
NULL, /* descrgetfunc tp_descr_get; */
@@ -2863,14 +3011,20 @@ static PyObject* pyrna_srna_ExternalType(StructRNA *srna)
/* sanity check, could skip this unless in debug mode */
if(newclass) {
PyObject *base_compare= pyrna_srna_PyBase(srna);
- PyObject *bases= PyObject_GetAttrString(newclass, "__bases__");
-
- if(PyTuple_GET_SIZE(bases)) {
+ //PyObject *slots= PyObject_GetAttrString(newclass, "__slots__"); // cant do this because it gets superclasses values!
+ //PyObject *bases= PyObject_GetAttrString(newclass, "__bases__"); // can do this but faster not to.
+ PyObject *bases= ((PyTypeObject *)newclass)->tp_bases;
+ PyObject *slots = PyDict_GetItemString(((PyTypeObject *)newclass)->tp_dict, "__slots__");
+
+ if(slots==NULL) {
+ fprintf(stderr, "pyrna_srna_ExternalType: expected class '%s' to have __slots__ defined\n\nSee bpy_types.py\n", idname);
+ newclass= NULL;
+ }
+ else if(PyTuple_GET_SIZE(bases)) {
PyObject *base= PyTuple_GET_ITEM(bases, 0);
if(base_compare != base) {
- PyLineSpit();
- fprintf(stderr, "pyrna_srna_ExternalType: incorrect subclassing of SRNA '%s'\n", idname);
+ fprintf(stderr, "pyrna_srna_ExternalType: incorrect subclassing of SRNA '%s'\nSee bpy_types.py\n", idname);
PyObSpit("Expected! ", base_compare);
newclass= NULL;
}
@@ -2879,8 +3033,6 @@ static PyObject* pyrna_srna_ExternalType(StructRNA *srna)
fprintf(stderr, "SRNA Subclassed: '%s'\n", idname);
}
}
-
- Py_DECREF(bases);
}
return newclass;
@@ -2913,13 +3065,12 @@ static PyObject* pyrna_srna_Subtype(StructRNA *srna)
if(!descr) descr= "(no docs)";
/* always use O not N when calling, N causes refcount errors */
- newclass = PyObject_CallFunction( (PyObject*)&PyType_Type, "s(O){ssss}", idname, py_base, "__module__","bpy.types", "__doc__",descr);
+ newclass = PyObject_CallFunction( (PyObject*)&PyType_Type, "s(O){sssss()}", idname, py_base, "__module__","bpy.types", "__doc__",descr, "__slots__");
/* newclass will now have 2 ref's, ???, probably 1 is internal since decrefing here segfaults */
/* PyObSpit("new class ref", newclass); */
if (newclass) {
-
/* srna owns one, and the other is owned by the caller */
pyrna_subtype_set_rna(newclass, srna);
@@ -3494,7 +3645,7 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
else if(srna) {
static char *kwlist[] = {"attr", "type", "name", "description", "hidden", NULL};
char *id=NULL, *name="", *description="";
- int hidden;
+ int hidden= 0;
PropertyRNA *prop;
StructRNA *ptype;
PyObject *type= Py_None;
@@ -3533,7 +3684,7 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
else if(srna) {
static char *kwlist[] = {"attr", "type", "name", "description", "hidden", NULL};
char *id=NULL, *name="", *description="";
- int hidden;
+ int hidden= 0;
PropertyRNA *prop;
StructRNA *ptype;
PyObject *type= Py_None;
@@ -3564,11 +3715,15 @@ static int deferred_register_prop(StructRNA *srna, PyObject *item, PyObject *key
if(PyTuple_CheckExact(item) && PyTuple_GET_SIZE(item)==2) {
PyObject *py_func_ptr, *py_kw, *py_srna_cobject, *py_ret;
+ PyObject *(*pyfunc)(PyObject *, PyObject *, PyObject *);
if(PyArg_ParseTuple(item, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
- PyObject *(*pyfunc)(PyObject *, PyObject *, PyObject *);
-
+ if(*_PyUnicode_AsString(key)=='_') {
+ PyErr_Format(PyExc_ValueError, "StructRNA \"%.200s\" registration error: %.200s could not register because the property starts with an '_'\n", RNA_struct_identifier(srna), _PyUnicode_AsString(key));
+ Py_DECREF(dummy_args);
+ return -1;
+ }
pyfunc = PyCObject_AsVoidPtr(py_func_ptr);
py_srna_cobject = PyCObject_FromVoidPtr(srna, NULL);
@@ -3669,13 +3824,12 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
PyObject *item, *fitem;
PyObject *py_arg_count;
int i, flag, arg_count, func_arg_count;
- const char *identifier;
+ const char *py_class_name = ((PyTypeObject *)py_class)->tp_name; // __name__
+
if (base_class) {
if (!PyObject_IsSubclass(py_class, base_class)) {
- PyObject *name= PyObject_GetAttrString(base_class, "__name__");
- PyErr_Format( PyExc_TypeError, "expected %.200s subclass of class \"%.200s\"", class_type, name ? _PyUnicode_AsString(name):"<UNKNOWN>");
- Py_XDECREF(name);
+ PyErr_Format( PyExc_TypeError, "expected %.200s subclass of class \"%.200s\"", class_type, py_class_name);
return -1;
}
}
@@ -3697,7 +3851,7 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
if (item==NULL) {
if ((flag & FUNC_REGISTER_OPTIONAL)==0) {
- PyErr_Format( PyExc_AttributeError, "expected %.200s class to have an \"%.200s\" attribute", class_type, RNA_function_identifier(func));
+ PyErr_Format( PyExc_AttributeError, "expected %.200s, %.200s class to have an \"%.200s\" attribute", class_type, py_class_name, RNA_function_identifier(func));
return -1;
}
@@ -3712,7 +3866,7 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
fitem= item; /* py 3.x */
if (PyFunction_Check(fitem)==0) {
- PyErr_Format( PyExc_TypeError, "expected %.200s class \"%.200s\" attribute to be a function", class_type, RNA_function_identifier(func));
+ PyErr_Format( PyExc_TypeError, "expected %.200s, %.200s class \"%.200s\" attribute to be a function", class_type, py_class_name, RNA_function_identifier(func));
return -1;
}
@@ -3724,7 +3878,7 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
Py_DECREF(py_arg_count);
if (arg_count != func_arg_count) {
- PyErr_Format( PyExc_AttributeError, "expected %.200s class \"%.200s\" function to have %d args", class_type, RNA_function_identifier(func), func_arg_count);
+ PyErr_Format( PyExc_AttributeError, "expected %.200s, %.200s class \"%.200s\" function to have %d args", class_type, py_class_name, RNA_function_identifier(func), func_arg_count);
return -1;
}
}
@@ -3734,6 +3888,7 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
/* verify properties */
lb= RNA_struct_defined_properties(srna);
for(link=lb->first; link; link=link->next) {
+ const char *identifier;
prop= (PropertyRNA*)link;
flag= RNA_property_flag(prop);
@@ -3757,9 +3912,20 @@ static int bpy_class_validate(PointerRNA *dummyptr, void *py_data, int *have_fun
}
}
+#if 0
+ if(strcmp(identifier, "bl_label") == 0) {
+ item= PyObject_GetAttrString(py_class, "__doc__");
+
+ if(item) {
+ Py_DECREF(item); /* no need to keep a ref, the class owns it */
+ if(pyrna_py_to_prop(dummyptr, prop, NULL, item, "validating class error:") != 0)
+ return -1;
+ }
+ }
+#endif
if (item == NULL && (((flag & PROP_REGISTER_OPTIONAL) != PROP_REGISTER_OPTIONAL))) {
- PyErr_Format( PyExc_AttributeError, "expected %.200s class to have an \"%.200s\" attribute", class_type, identifier);
+ PyErr_Format( PyExc_AttributeError, "expected %.200s, %.200s class to have an \"%.200s\" attribute", class_type, py_class_name, identifier);
return -1;
}
@@ -3937,6 +4103,7 @@ void pyrna_free_types(void)
}
}
RNA_PROP_END;
+
}
/* Note! MemLeak XXX
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 0e40bf7258c..37f6af36726 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -103,5 +103,6 @@ int pyrna_py_to_array_index(PointerRNA *ptr, PropertyRNA *prop, int arraydim, in
PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop);
PyObject *pyrna_py_from_array_index(BPy_PropertyRNA *self, int index);
PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop);
+int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value);
#endif
diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c
index 174d1aa342f..db3798146d3 100644
--- a/source/blender/python/intern/bpy_util.c
+++ b/source/blender/python/intern/bpy_util.c
@@ -92,11 +92,13 @@ int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag)
if(cstring) {
fd= flagdef;
while(fd->name) {
- if (strcmp(cstring, fd->name) == 0)
+ if (strcmp(cstring, fd->name) == 0) {
(*flag) |= fd->flag;
+ break;
+ }
fd++;
}
- if (fd==NULL) { /* could not find a match */
+ if (fd->name==NULL) { /* could not find a match */
error_val= 1;
}
} else {
@@ -416,6 +418,7 @@ int BPy_reports_to_error(ReportList *reports)
int BPy_errors_to_report(ReportList *reports)
{
PyObject *pystring;
+ PyObject *pystring_format= NULL; // workaround, see below
char *cstring;
char *filename;
@@ -439,14 +442,23 @@ int BPy_errors_to_report(ReportList *reports)
}
BPY_getFileAndNum(&filename, &lineno);
+ if(filename==NULL)
+ filename= "<unknown location>";
cstring= _PyUnicode_AsString(pystring);
-
+
+#if 0 // ARG!. workaround for a bug in blenders use of vsnprintf
BKE_reportf(reports, RPT_ERROR, "%s\nlocation:%s:%d\n", cstring, filename, lineno);
+#else
+ pystring_format= PyUnicode_FromFormat("%s\nlocation:%s:%d\n", cstring, filename, lineno);
+ cstring= _PyUnicode_AsString(pystring_format);
+ BKE_report(reports, RPT_ERROR, cstring);
+#endif
fprintf(stderr, "%s\nlocation:%s:%d\n", cstring, filename, lineno); // not exactly needed. just for testing
Py_DECREF(pystring);
+ Py_DECREF(pystring_format); // workaround
return 1;
}
diff --git a/source/blender/python/sphinx_doc_gen.py b/source/blender/python/sphinx_doc_gen.py
new file mode 100644
index 00000000000..24910b911e4
--- /dev/null
+++ b/source/blender/python/sphinx_doc_gen.py
@@ -0,0 +1,322 @@
+ # ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ #
+ # Contributor(s): Campbell Barton
+ #
+ # #**** END GPL LICENSE BLOCK #****
+
+script_help_msg = '''
+Usage,
+run this script from blenders root path once you have compiled blender
+ ./blender.bin -b -P /b/source/blender/python/sphinx_doc_gen.py
+
+This will generate python files in "./source/blender/python/doc/bpy/sphinx-in"
+Generate html docs by running...
+
+ sphinx-build source/blender/python/doc/bpy/sphinx-in source/blender/python/doc/bpy/sphinx-out
+'''
+
+# if you dont have graphvis installed ommit the --graph arg.
+
+# GLOBALS['BASEDIR'] = './source/blender/python/doc'
+
+import os
+import bpy
+import rna_info
+reload(rna_info)
+
+def range_str(val):
+ if val < -10000000: return '-inf'
+ if val > 10000000: return 'inf'
+ if type(val)==float:
+ return '%g' % val
+ else:
+ return str(val)
+
+def write_indented_lines(ident, fn, text):
+ if text is None:
+ return
+ for l in text.split("\n"):
+ fn(ident + l.strip() + "\n")
+
+
+def py_function_args(func):
+ """Get the python functions args with keyword defaults where possible."""
+ text = ""
+ code = func.__code__
+
+ defaults = func.__defaults__
+ if defaults is None:
+ defaults = []
+ else:
+ defaults = list(defaults)
+
+ names = code.co_varnames
+ if names is None:
+ names = []
+ else:
+ names = list(names)
+
+ INVALID = py_function_args
+ defaults = ([INVALID] * (len(names) - len(defaults))) + defaults
+
+ if names[0] in ("self", "cls"): # normal class function or classmethod
+ del names[0]
+ del defaults[0]
+
+ for var, default in zip(names, defaults):
+ if default is INVALID:
+ text += var
+ else:
+ if type(default) == str:
+ text += "%s=\"%s\"" % (var, default)
+ elif type(default) == float:
+ text += rna_info.float_as_string(default)
+ else:
+ text += "%s=%s" % (var, repr(default))
+
+ if not var == names[-1]:
+ text += ", "
+
+ return text
+
+
+def rna2sphinx(BASEPATH):
+
+ structs, funcs, ops, props = rna_info.BuildRNAInfo()
+
+ try:
+ os.mkdir(BASEPATH)
+ except:
+ pass
+
+ # conf.py - empty for now
+ filepath = os.path.join(BASEPATH, "conf.py")
+ file = open(filepath, "w")
+ fw = file.write
+
+ fw("project = 'Blender 3D'\n")
+ # fw("master_doc = 'index'\n")
+ fw("copyright = u'Blender Foundation'\n")
+ fw("version = '2.5'\n")
+ fw("release = '2.5'\n")
+ file.close()
+
+
+ filepath = os.path.join(BASEPATH, "contents.rst")
+ file = open(filepath, "w")
+ fw = file.write
+
+ fw("\n")
+ fw(".. toctree::\n")
+ fw(" :glob:\n\n")
+ fw(" bpy.ops.*\n\n")
+ fw(" bpy.types.*\n\n")
+ file.close()
+
+ if 0:
+ filepath = os.path.join(BASEPATH, "bpy.rst")
+ file = open(filepath, "w")
+ fw = file.write
+
+ fw("\n")
+
+ title = ":mod:`bpy` --- Blender Python Module"
+ fw("%s\n%s\n\n" % (title, "=" * len(title)))
+ fw(".. module:: bpy.types\n\n")
+ file.close()
+
+
+ def write_property(ident, fw, prop, as_arg=False):
+
+ if prop.description:
+ fw(ident + " %s\n\n" % prop.description)
+
+ if prop.fixed_type is None:
+ fw(ident + " *type* %s " % prop.type)
+ if prop.array_length:
+ fw("array of %d items " % (prop.array_length))
+
+ if prop.type in ("float", "int"):
+ fw("in [%s, %s]" % (range_str(prop.min), range_str(prop.max)))
+ elif prop.type == "enum":
+ fw("in [%s]" % ', '.join(["`" + s + "`" for s in prop.enum_items]))
+ else:
+ if prop.type == "collection":
+ if prop.collection_type:
+ collection_str = ":class:`%s` collection of " % prop.collection_type.identifier
+ else:
+ collection_str = "Collection of "
+ else:
+ collection_str = " "
+
+ fw(ident + " *type* %s:class:`%s`" % (collection_str, prop.fixed_type.identifier))
+
+ if not as_arg: # readonly is only useful for props, not args
+ if prop.is_readonly:
+ fw(", (readonly)")
+
+ if prop.is_never_none:
+ fw(", (never None)")
+
+ fw("\n\n")
+
+
+ def write_struct(struct):
+ #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
+ # return
+
+ #if not struct.identifier.startswith("Bone"):
+ # return
+
+ filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier)
+ file = open(filepath, "w")
+ fw = file.write
+
+ if struct.base:
+ title = "%s(%s)" % (struct.identifier, struct.base.identifier)
+ else:
+ title = struct.identifier
+
+ fw("%s\n%s\n\n" % (title, "=" * len(title)))
+
+ fw(".. module:: bpy.types\n\n")
+
+ bases = struct.get_bases()
+ if bases:
+ if len(bases) > 1:
+ fw("base classes --- ")
+ else:
+ fw("base class --- ")
+
+ fw(", ".join([(":class:`%s`" % base.identifier) for base in reversed(bases)]))
+ fw("\n\n")
+
+ subclasses = [s for s in structs.values() if s.base is struct]
+
+ if subclasses:
+ fw("subclasses --- \n")
+ fw(", ".join([(":class:`%s`" % s.identifier) for s in subclasses]))
+ fw("\n\n")
+
+
+ if struct.base:
+ fw(".. class:: %s(%s)\n\n" % (struct.identifier, struct.base.identifier))
+ else:
+ fw(".. class:: %s\n\n" % struct.identifier)
+
+ fw(" %s\n\n" % struct.description)
+
+ for prop in struct.properties:
+ fw(" .. attribute:: %s\n\n" % prop.identifier)
+ write_property(" ", fw, prop)
+
+ # python attributes
+ py_properties = struct.get_py_properties()
+ py_prop = None
+ for identifier, py_prop in py_properties:
+ fw(" .. attribute:: %s\n\n" % identifier)
+ write_indented_lines(" ", fw, py_prop.__doc__)
+ if py_prop.fset is None:
+ fw(" (readonly)\n\n")
+ del py_properties, py_prop
+
+ for func in struct.functions:
+ args_str = ", ".join([prop.get_arg_default(force=False) for prop in func.args])
+
+ fw(" .. method:: %s(%s)\n\n" % (func.identifier, args_str))
+ fw(" %s\n\n" % func.description)
+
+ for prop in func.args:
+ fw(" * %s: %s\n" % (prop.identifier, prop.name))
+ write_property(" ", fw, prop, as_arg=True)
+
+ if func.return_value:
+ prop = func.return_value
+ fw(" Returns %s: %s\n" % (prop.identifier, prop.name))
+ write_property(" ", fw, prop, as_arg=True)
+
+
+ # python methods
+ py_funcs = struct.get_py_functions()
+ py_func = None
+
+ for identifier, py_func in py_funcs:
+ fw(" .. method:: %s(%s)\n\n" % (identifier, py_function_args(py_func)))
+ write_indented_lines(" ", fw, py_func.__doc__)
+ del py_funcs, py_func
+
+ if struct.references:
+ # use this otherwise it gets in the index for a normal heading.
+ fw(".. rubric:: References\n\n")
+
+ for ref in struct.references:
+ ref_split = ref.split(".")
+ if len(ref_split) > 2:
+ ref = ref_split[-2] + "." + ref_split[-1]
+ fw("* :class:`%s`\n" % ref)
+ fw("\n")
+
+
+ for struct in structs.values():
+ write_struct(struct)
+
+ # oeprators
+ def write_ops():
+ fw = None
+
+ last_mod = ''
+
+ for op_key in sorted(ops.keys()):
+ op = ops[op_key]
+
+ if last_mod != op.module_name:
+ filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op.module_name)
+ file = open(filepath, "w")
+ fw = file.write
+
+ title = "%s Operators" % (op.module_name[0].upper() + op.module_name[1:])
+ fw("%s\n%s\n\n" % (title, "=" * len(title)))
+
+ fw(".. module:: bpy.ops.%s\n\n" % op.module_name)
+ last_mod = op.module_name
+
+ args_str = ", ".join([prop.get_arg_default(force=True) for prop in op.args])
+ fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
+ fw(" %s\n\n" % op.description)
+ for prop in op.args:
+ fw(" * %s: %s\n" % (prop.identifier, prop.name))
+ write_property(" ", fw, prop, as_arg=True)
+
+ location = op.get_location()
+ if location != (None, None):
+ fw(" *python operator source --- `%s:%d`* \n\n" % location)
+
+ write_ops()
+
+ file.close()
+
+if __name__ == '__main__':
+ if 'bpy' not in dir():
+ print("\nError, this script must run from inside blender2.5")
+ print(script_help_msg)
+ else:
+ # os.system("rm source/blender/python/doc/bpy/sphinx-in/*.rst")
+ # os.system("rm -rf source/blender/python/doc/bpy/sphinx-out/*")
+ rna2sphinx('source/blender/python/doc/bpy/sphinx-in')
+
+ import sys
+ sys.exit()