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:
authorCampbell Barton <ideasman42@gmail.com>2009-04-01 16:43:07 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-04-01 16:43:07 +0400
commit3224efc38419201b44503982541aca8eaeb29486 (patch)
tree77a09bed4e1668427c48ceac7fdb8021eaa7f729 /source/blender/python/intern
parent3a28a7450596f46281431bd163b9a237eb481055 (diff)
Python Panels WIP
- Register python panels - Added a generic class checking function BPY_class_validate() for panels/operators. - No button drawing yet Brecht, Added RNA_enum_value_from_id() and RNA_enum_id_from_value() to rna_access.c to do lookups between identifiers and values of EnumPropertyItem's, Not sure if these should go here.
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r--source/blender/python/intern/bpy_interface.c5
-rw-r--r--source/blender/python/intern/bpy_operator.c2
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.c (renamed from source/blender/python/intern/bpy_opwrapper.c)174
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.h (renamed from source/blender/python/intern/bpy_opwrapper.h)4
-rw-r--r--source/blender/python/intern/bpy_panel_wrap.h36
-rw-r--r--source/blender/python/intern/bpy_rna.c15
-rw-r--r--source/blender/python/intern/bpy_ui.c6
-rw-r--r--source/blender/python/intern/bpy_util.c89
-rw-r--r--source/blender/python/intern/bpy_util.h21
9 files changed, 221 insertions, 131 deletions
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 94ec8deebe4..f8031368f06 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -241,6 +241,11 @@ int BPY_run_script_space_listener(bContext *C, SpaceScript * sc)
return 1;
}
+void BPY_DECREF(void *pyob_ptr)
+{
+ Py_DECREF((PyObject *)pyob_ptr);
+}
+
#if 0
/* called from the the scripts window, assume context is ok */
int BPY_run_python_script_space(const char *modulename, const char *func)
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 02f4723c037..29ec3376765 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -24,7 +24,7 @@
*/
#include "bpy_operator.h"
-#include "bpy_opwrapper.h"
+#include "bpy_operator_wrap.h"
#include "bpy_rna.h" /* for setting arg props only - pyrna_py_to_prop() */
#include "bpy_compat.h"
diff --git a/source/blender/python/intern/bpy_opwrapper.c b/source/blender/python/intern/bpy_operator_wrap.c
index b7d4c82588e..1db1f547fa6 100644
--- a/source/blender/python/intern/bpy_opwrapper.c
+++ b/source/blender/python/intern/bpy_operator_wrap.c
@@ -24,7 +24,7 @@
*/
-#include "bpy_opwrapper.h"
+#include "bpy_operator_wrap.h"
#include "BLI_listbase.h"
#include "BKE_context.h"
#include "BKE_report.h"
@@ -45,14 +45,6 @@
#define PYOP_ATTR_IDNAME "__name__" /* use pythons class name */
#define PYOP_ATTR_DESCRIPTION "__doc__" /* use pythons docstring */
-typedef struct PyOperatorType {
- void *next, *prev;
- char idname[OP_MAX_TYPENAME];
- char name[OP_MAX_TYPENAME];
- char description[OP_MAX_TYPENAME]; // XXX should be longer?
- PyObject *py_class;
-} PyOperatorType;
-
static PyObject *pyop_dict_from_event(wmEvent *event)
{
PyObject *dict= PyDict_New();
@@ -191,14 +183,16 @@ static struct BPY_flag_def pyop_ret_flags[] = {
static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *event)
{
- PyOperatorType *pyot = op->type->pyop_data;
+ PyObject *py_class = op->type->pyop_data;
PyObject *args;
PyObject *ret= NULL, *py_class_instance, *item;
int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
-
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
args = PyTuple_New(1);
- PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(pyot->py_class, "__rna__")); // need to use an rna instance as the first arg
- py_class_instance = PyObject_Call(pyot->py_class, args, NULL);
+ PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(py_class, "__rna__")); // need to use an rna instance as the first arg
+ 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 */
@@ -229,16 +223,16 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
if (mode==PYOP_INVOKE) {
- item= PyObject_GetAttrString(pyot->py_class, "invoke");
+ item= PyObject_GetAttrString(py_class, "invoke");
args = PyTuple_New(2);
PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event));
}
else if (mode==PYOP_EXEC) {
- item= PyObject_GetAttrString(pyot->py_class, "exec");
+ item= PyObject_GetAttrString(py_class, "exec");
args = PyTuple_New(1);
}
else if (mode==PYOP_POLL) {
- item= PyObject_GetAttrString(pyot->py_class, "poll");
+ item= PyObject_GetAttrString(py_class, "poll");
args = PyTuple_New(2);
//XXX Todo - wrap context in a useful way, None for now.
PyTuple_SET_ITEM(args, 1, Py_None);
@@ -251,7 +245,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
Py_DECREF(item);
}
- if (ret == NULL) {
+ if (ret == NULL) { /* covers py_class_instance failing too */
pyop_error_report(op->reports);
}
else {
@@ -280,6 +274,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
Py_DECREF(ret);
}
+ PyGILState_Release(gilstate);
+
return ret_flag;
}
@@ -302,15 +298,29 @@ static int PYTHON_OT_poll(bContext *C)
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
{
- PyOperatorType *pyot = (PyOperatorType *)userdata;
- PyObject *py_class = pyot->py_class;
+ PyObject *py_class = (PyObject *)userdata;
PyObject *props, *item;
/* identifiers */
- ot->name= pyot->name;
- ot->idname= pyot->idname;
- ot->description= pyot->description;
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
+ Py_DECREF(item);
+ ot->idname= _PyUnicode_AsString(item);
+
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_UINAME);
+ if (item) {
+ Py_DECREF(item);
+ ot->name= _PyUnicode_AsString(item);
+ }
+ else {
+ ot->name= ot->idname;
+ PyErr_Clear();
+ }
+
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_DESCRIPTION);
+ Py_DECREF(item);
+ ot->description= (item && PyUnicode_Check(item)) ? _PyUnicode_AsString(item):"";
+
/* api callbacks, detailed checks dont on adding */
if (PyObject_HasAttrString(py_class, "invoke"))
ot->invoke= PYTHON_OT_invoke;
@@ -364,105 +374,48 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
/* pyOperators - Operators defined IN Python */
-PyObject *PYOP_wrap_add(PyObject *self, PyObject *value)
+PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
{
- PyObject *optype, *item;
+ PyObject *base_class, *item;
- PyOperatorType *pyot;
- char *idname= NULL;
- char *name= NULL;
- char *description= NULL;
- static char *pyop_func_names[] = {"exec", "invoke", "poll", NULL};
- static int pyop_func_nargs[] = {1, 2, 2, 0};
-
- PyObject *pyargcount;
- int i, argcount;
-
+ char *idname= NULL;
+ int i;
+
+ static struct BPY_class_attr_check pyop_class_attr_values[]= {
+ {PYOP_ATTR_IDNAME, 's', 0, 0},
+ {PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL},
+ {PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL},
+ {PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK},
+ {"exec", 'f', 1, BPY_CLASS_ATTR_OPTIONAL},
+ {"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL},
+ {"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL},
+ {NULL, 0, 0, 0}
+ };
// in python would be...
//PyObject *optype = PyObject_GetAttrString(PyObject_GetAttrString(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), "types"), "Operator");
- optype = PyObject_GetAttrStringArgs(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), 2, "types", "Operator");
- Py_DECREF(optype);
+ base_class = PyObject_GetAttrStringArgs(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), 2, "types", "Operator");
+ Py_DECREF(base_class);
-
- if (!PyObject_IsSubclass(value, optype)) {
- PyErr_SetString( PyExc_AttributeError, "expected Operator subclass of bpy.types.Operator");
- return NULL;
+ if(BPY_class_validate("Operator", py_class, base_class, pyop_class_attr_values, NULL) < 0) {
+ return NULL; /* BPY_class_validate sets the error */
}
-
+
/* class name is used for operator ID - this can be changed later if we want */
- item = PyObject_GetAttrString(value, PYOP_ATTR_IDNAME);
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
Py_DECREF(item);
idname = _PyUnicode_AsString(item);
-
if (WM_operatortype_find(idname)) {
PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name \"%s\"", idname);
return NULL;
}
- /* Operator user readible name */
- item = PyObject_GetAttrString(value, PYOP_ATTR_UINAME);
- if (item) {
- Py_DECREF(item);
- name = _PyUnicode_AsString(item);
- }
- if (name == NULL) {
- name = idname;
- PyErr_Clear();
- }
-
- /* use py docstring for description, should always be None or a string */
- item = PyObject_GetAttrString(value, PYOP_ATTR_DESCRIPTION);
- Py_DECREF(item);
-
- if (PyUnicode_Check(item)) {
- description = _PyUnicode_AsString(item);
- }
- else {
- description = "";
- }
-
- /* Check known functions and argument lengths */
- for (i=0; pyop_func_names[i]; i++) {
-
- item=PyObject_GetAttrString(value, pyop_func_names[i]);
- if (item) {
- Py_DECREF(item);
-
- /* check its callable */
- if (!PyFunction_Check(item)) {
- PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() is not a function", idname, pyop_func_names[i]);
- return NULL;
- }
- /* check the number of args is correct */
- /* MyClass.exec.func_code.co_argcount */
-
- pyargcount = PyObject_GetAttrString(PyFunction_GET_CODE(item), "co_argcount");
- Py_DECREF(pyargcount);
- argcount = PyLong_AsSsize_t(pyargcount);
-
- if (argcount != pyop_func_nargs[i]) {
- PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() takes %d args, should be %d", idname, pyop_func_names[i], argcount, pyop_func_nargs[i]);
- return NULL;
- }
-
- } else {
- PyErr_Clear();
- }
- }
-
/* If we have properties set, check its a list of dicts */
- item = PyObject_GetAttrString(value, PYOP_ATTR_PROP);
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_PROP);
if (item) {
Py_DECREF(item);
-
- if (!PyList_Check(item)) {
- PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must be a list", idname);
- return NULL;
- }
-
for(i=0; i<PyList_Size(item); i++) {
PyObject *py_args = PyList_GET_ITEM(item, i);
PyObject *py_func_ptr, *py_kw; /* place holders */
@@ -477,24 +430,18 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *value)
PyErr_Clear();
}
- pyot= MEM_callocN(sizeof(PyOperatorType), "PyOperatorType");
-
- strncpy(pyot->idname, idname, sizeof(pyot->idname));
- strncpy(pyot->name, name, sizeof(pyot->name));
- strncpy(pyot->description, description, sizeof(pyot->description));
- pyot->py_class= value;
- Py_INCREF(value);
-
- WM_operatortype_append_ptr(PYTHON_OT_wrapper, pyot);
+ Py_INCREF(py_class);
+ WM_operatortype_append_ptr(PYTHON_OT_wrapper, py_class);
Py_RETURN_NONE;
}
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
{
+ PyObject *py_class;
char *idname= NULL;
wmOperatorType *ot;
- PyOperatorType *pyot;
+
if (PyUnicode_Check(value))
idname = _PyUnicode_AsString(value);
@@ -514,13 +461,12 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
return NULL;
}
- if (!(pyot= (PyOperatorType *)ot->pyop_data)) {
+ if (!(py_class= (PyObject *)ot->pyop_data)) {
PyErr_Format( PyExc_AttributeError, "Operator \"%s\" was not created by python", idname);
return NULL;
}
- Py_XDECREF(pyot->py_class);
- MEM_freeN(pyot);
+ Py_XDECREF(py_class);
WM_operatortype_remove(idname);
diff --git a/source/blender/python/intern/bpy_opwrapper.h b/source/blender/python/intern/bpy_operator_wrap.h
index 04120a81517..2929d57ab82 100644
--- a/source/blender/python/intern/bpy_opwrapper.h
+++ b/source/blender/python/intern/bpy_operator_wrap.h
@@ -22,8 +22,8 @@
*
* ***** END GPL LICENSE BLOCK *****
*/
-#ifndef BPY_OPWRAPPER_H
-#define BPY_OPWRAPPER_H
+#ifndef BPY_OPERATOR_WRAP_H
+#define BPY_OPERATOR_WRAP_H
#include <Python.h>
diff --git a/source/blender/python/intern/bpy_panel_wrap.h b/source/blender/python/intern/bpy_panel_wrap.h
new file mode 100644
index 00000000000..42b24703d00
--- /dev/null
+++ b/source/blender/python/intern/bpy_panel_wrap.h
@@ -0,0 +1,36 @@
+
+/**
+ * $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): Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef BPY_PANEL_WRAP_H
+#define BPY_PANEL_WRAP_H
+
+#include <Python.h>
+
+/* these are used for operator methods, used by bpy_operator.c */
+
+PyObject *PyPanel_wrap_add(PyObject *self, PyObject *args);
+PyObject *PyPanel_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 23eed552a2a..20ca134c67e 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -128,20 +128,9 @@ static char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop)
{
const EnumPropertyItem *item;
int totitem, i;
-
- DynStr *dynstr= BLI_dynstr_new();
- char *cstring;
-
- RNA_property_enum_items(ptr, prop, &item, &totitem);
-
- for (i=0; i<totitem; i++) {
-
- BLI_dynstr_appendf(dynstr, i?", '%s'":"'%s'", item[i].identifier);
- }
- cstring = BLI_dynstr_get_cstring(dynstr);
- BLI_dynstr_free(dynstr);
- return cstring;
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+ return BPy_enum_as_string(item);
}
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
diff --git a/source/blender/python/intern/bpy_ui.c b/source/blender/python/intern/bpy_ui.c
index 7b3b4c01c55..608086843ba 100644
--- a/source/blender/python/intern/bpy_ui.c
+++ b/source/blender/python/intern/bpy_ui.c
@@ -27,6 +27,8 @@
#include "bpy_rna.h" /* for rna buttons */
#include "bpy_operator.h" /* for setting button operator properties */
#include "bpy_compat.h"
+#include "bpy_panel_wrap.h" /* for setting button operator properties */
+
#include "WM_types.h" /* for WM_OP_INVOKE_DEFAULT & friends */
#include "BLI_dynstr.h"
@@ -423,6 +425,10 @@ static struct PyMethodDef ui_methods[] = {
{"getScreenPtr", (PyCFunction)Method_getScreenPtr, METH_NOARGS, ""},
{"getSpacePtr", (PyCFunction)Method_getSpacePtr, METH_NOARGS, ""},
{"getWindowPtr", (PyCFunction)Method_getWindowPtr, METH_NOARGS, ""},
+
+ /* Adding panels should be moved, at the moment there is no obvious place as there is with operators */
+ {"addPanel", (PyCFunction)PyPanel_wrap_add, METH_VARARGS, ""},
+ {"removePanel", (PyCFunction)PyPanel_wrap_remove, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};
diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c
index 7979ca9cd37..347b914a030 100644
--- a/source/blender/python/intern/bpy_util.c
+++ b/source/blender/python/intern/bpy_util.c
@@ -26,7 +26,6 @@
#include "bpy_util.h"
#include "BLI_dynstr.h"
#include "MEM_guardedalloc.h"
-#include "bpy_compat.h"
PyObject *BPY_flag_to_list(struct BPY_flag_def *flagdef, int flag)
{
@@ -241,3 +240,91 @@ PyObject *PyObject_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...)
Py_XINCREF(item); /* final value has is increfed, to match PyObject_GetAttrString */
return item;
}
+
+
+int BPY_class_validate(const char *class_type, PyObject *class, PyObject *base_class, BPY_class_attr_check* class_attrs, PyObject **py_class_attrs)
+{
+ PyObject *item;
+ PyObject *py_arg_count;
+ int i, arg_count;
+
+ if (base_class) {
+ if (!PyObject_IsSubclass(class, base_class)) {
+ PyObject *name= PyObject_GetAttrString(base_class, "__name__");
+ PyErr_Format( PyExc_AttributeError, "expected %s subclass of class \"%s\"", class_type, name ? _PyUnicode_AsString(name):"<UNKNOWN>");
+ Py_XDECREF(name);
+ return -1;
+ }
+ }
+
+ for(i= 0;class_attrs->name; class_attrs++, i++) {
+ item = PyObject_GetAttrString(class, class_attrs->name);
+
+ if (py_class_attrs)
+ py_class_attrs[i]= item;
+
+ if (item==NULL) {
+ if ((class_attrs->flag & BPY_CLASS_ATTR_OPTIONAL)==0) {
+ PyErr_Format( PyExc_AttributeError, "expected %s class to have an \"%s\" attribute", class_type, class_attrs->name);
+ return -1;
+ }
+
+ PyErr_Clear();
+ }
+ else {
+ Py_DECREF(item); /* no need to keep a ref, the class owns it */
+
+ if((item==Py_None) && (class_attrs->flag & BPY_CLASS_ATTR_NONE_OK)) {
+ /* dont do anything, this is ok, dont bother checking other types */
+ }
+ else {
+ switch(class_attrs->type) {
+ case 's':
+ if (PyUnicode_Check(item)==0) {
+ PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a string", class_type, class_attrs->name);
+ return -1;
+ }
+ break;
+ case 'l':
+ if (PyList_Check(item)==0) {
+ PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a list", class_type, class_attrs->name);
+ return -1;
+ }
+ break;
+ case 'f':
+ if (PyFunction_Check(item)==0) {
+ PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a function", class_type, class_attrs->name);
+ return -1;
+ }
+ if (class_attrs->arg_count >= 0) { /* -1 if we dont care*/
+ py_arg_count = PyObject_GetAttrString(PyFunction_GET_CODE(item), "co_argcount");
+ arg_count = PyLong_AsSsize_t(py_arg_count);
+ Py_DECREF(py_arg_count);
+
+ if (arg_count != class_attrs->arg_count) {
+ PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" function to have %d args", class_type, class_attrs->name, class_attrs->arg_count);
+ return -1;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+char *BPy_enum_as_string(EnumPropertyItem *item)
+{
+ DynStr *dynstr= BLI_dynstr_new();
+ EnumPropertyItem *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;
+}
diff --git a/source/blender/python/intern/bpy_util.h b/source/blender/python/intern/bpy_util.h
index 51e13f98a35..e2bea331b0e 100644
--- a/source/blender/python/intern/bpy_util.h
+++ b/source/blender/python/intern/bpy_util.h
@@ -28,6 +28,9 @@
#define BPY_UTIL_H
#include "bpy_compat.h"
+#include "RNA_types.h" /* for EnumPropertyItem only */
+
+struct EnumPropertyItem;
/* for internal use only, so python can interchange a sequence of strings with flags */
typedef struct BPY_flag_def {
@@ -46,4 +49,22 @@ void BPY_getFileAndNum(char **filename, int *lineno);
/* own python like utility function */
PyObject *PyObject_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...);
+
+
+/* Class type checking, use for checking classes can be added as operators, panels etc */
+typedef struct BPY_class_attr_check {
+ const char *name; /* name of the class attribute */
+ char type; /* 's' = string, 'f' = function, 'l' = list, (add as needed) */
+ int arg_count; /* only for function types, -1 for undefined, includes self arg */
+ int flag; /* other options */
+} BPY_class_attr_check;
+
+/* BPY_class_attr_check, flag */
+#define BPY_CLASS_ATTR_OPTIONAL 1
+#define BPY_CLASS_ATTR_NONE_OK 2
+
+int BPY_class_validate(const char *class_type, PyObject *class, PyObject *base_class, BPY_class_attr_check* class_attrs, PyObject **py_class_attrs);
+
+char *BPy_enum_as_string(struct EnumPropertyItem *item);
+
#endif