You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

547 lines
25 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
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 3 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, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef _pyGlueHelpers_h_
#define _pyGlueHelpers_h_
class plString;
class pyKey;
typedef struct _object PyObject;
typedef struct _typeobject PyTypeObject;
typedef struct PyMethodDef PyMethodDef;
// Useful string functions
plString PyString_AsStringEx(PyObject* obj);
bool PyString_CheckEx(PyObject* obj);
PyObject* PyUnicode_FromStringEx(const plString& str);
// A set of macros to take at least some of the tediousness out of creating straight python glue code
/////////////////////////////////////////////////////////////////////
// Python class definition macros
/////////////////////////////////////////////////////////////////////
// This defines the basic PyObject we need for python classes
#define PYTHON_CLASS_DEFINITION(pythonClassName, glueClassName) \
struct pythonClassName \
{ \
PyObject_HEAD \
glueClassName *fThis; \
}
// This makes sure that our python new function can access our constructors
#define PYTHON_CLASS_NEW_FRIEND(pythonClassName) friend PyObject *pythonClassName##_new(PyTypeObject *type, PyObject *args, PyObject *keywords)
// This defines the basic new function for a class
#define PYTHON_CLASS_NEW_DEFINITION static PyObject *New()
#define PYTHON_CLASS_NEW_IMPL(pythonClassName, glueClassName) \
PyObject *glueClassName::New() \
{ \
pythonClassName *newObj = (pythonClassName*)pythonClassName##_type.tp_new(&pythonClassName##_type, NULL, NULL); \
return (PyObject*)newObj; \
}
// This defines the basic check function for a class
#define PYTHON_CLASS_CHECK_DEFINITION static bool Check(PyObject *obj)
#define PYTHON_CLASS_CHECK_IMPL(pythonClassName, glueClassName) \
bool glueClassName::Check(PyObject *obj) \
{ \
return PyObject_TypeCheck(obj, &pythonClassName##_type); \
}
// This defines the basic convert from function for a class
#define PYTHON_CLASS_CONVERT_FROM_DEFINITION(glueClassName) static glueClassName *ConvertFrom(PyObject *obj)
#define PYTHON_CLASS_CONVERT_FROM_IMPL(pythonClassName, glueClassName) \
glueClassName *glueClassName::ConvertFrom(PyObject *obj) \
{ \
if (!Check(obj)) \
{ \
PyErr_SetString(PyExc_TypeError, "object is not a " #pythonClassName); \
return NULL; \
} \
return ((pythonClassName*)obj)->fThis; \
}
/////////////////////////////////////////////////////////////////////
// Python type definition macros
/////////////////////////////////////////////////////////////////////
// This starts off the type definition (however most of the data still needs to be filled in by hand
#define PYTHON_TYPE_START(pythonClassName) \
static PyTypeObject pythonClassName##_type = { \
PyObject_HEAD_INIT(NULL)
// and easy terminator to make things look pretty
#define PYTHON_TYPE_END }
// the default new function definition
#define PYTHON_DEFAULT_NEW_DEFINITION(pythonClassName, glueClassName) \
PyObject *pythonClassName##_new(PyTypeObject *type, PyObject *, PyObject *) \
{ \
pythonClassName *self; \
self = (pythonClassName*)type->tp_alloc(type, 0); \
if (self != NULL) \
{ \
self->fThis = new glueClassName(); \
if (self->fThis == NULL) \
{ \
Py_DECREF(self); \
return NULL; \
} \
} \
\
return (PyObject*)self; \
}
#define PYTHON_DEFAULT_NEW(pythonClassName) pythonClassName##_new
// the default dealloc function definition
#define PYTHON_DEFAULT_DEALLOC_DEFINITION(pythonClassName) \
void pythonClassName##_dealloc(pythonClassName *self) \
{ \
delete self->fThis; \
self->ob_type->tp_free((PyObject*)self); \
}
#define PYTHON_DEFAULT_DEALLOC(pythonClassName) (destructor)pythonClassName##_dealloc
// init function defines
#define PYTHON_RETURN_INIT_ERROR return -1;
#define PYTHON_RETURN_INIT_OK return 0;
// basic no init function stuff, for when you don't want the class to be created by python
#define PYTHON_NO_INIT_DEFINITION(pythonClassName) \
int pythonClassName##___init__(pythonClassName *, PyObject *, PyObject *) \
{ \
PyErr_SetString(PyExc_RuntimeError, "Cannot create " #pythonClassName " objects from Python"); \
PYTHON_RETURN_INIT_ERROR; \
}
#define PYTHON_INIT_DEFINITION(pythonClassName, argsVar, keywordsVar) \
int pythonClassName##___init__(pythonClassName *self, PyObject *args, PyObject *keywords)
#define PYTHON_DEFAULT_INIT(pythonClassName) (initproc)pythonClassName##___init__
// message table default name
#define PYTHON_DEFAULT_METHODS_TABLE(pythonClassName) pythonClassName##_methods
// most glue classes can get away with this default structure
#define PLASMA_DEFAULT_TYPE(pythonClassName, docString) \
PYTHON_TYPE_START(pythonClassName) \
0, /* ob_size */ \
"Plasma." #pythonClassName, /* tp_name */ \
sizeof(pythonClassName), /* tp_basicsize */ \
0, /* tp_itemsize */ \
PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \
0, /* tp_print */ \
0, /* tp_getattr */ \
0, /* tp_setattr */ \
0, /* tp_compare */ \
0, /* tp_repr */ \
0, /* tp_as_number */ \
0, /* tp_as_sequence */ \
0, /* tp_as_mapping */ \
0, /* tp_hash */ \
0, /* tp_call */ \
0, /* tp_str */ \
0, /* tp_getattro */ \
0, /* tp_setattro */ \
0, /* tp_as_buffer */ \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \
docString, /* tp_doc */ \
0, /* tp_traverse */ \
0, /* tp_clear */ \
0, /* tp_richcompare */ \
0, /* tp_weaklistoffset */ \
0, /* tp_iter */ \
0, /* tp_iternext */ \
PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \
0, /* tp_members */ \
0, /* tp_getset */ \
0, /* tp_base */ \
0, /* tp_dict */ \
0, /* tp_descr_get */ \
0, /* tp_descr_set */ \
0, /* tp_dictoffset */ \
PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \
0, /* tp_alloc */ \
PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \
PYTHON_TYPE_END
// default compare/rich compare function name
#define PYTHON_DEFAULT_COMPARE(pythonClassName) pythonClassName##_compare
#define PYTHON_NO_COMPARE 0
#define PYTHON_DEFAULT_RICH_COMPARE(pythonClassName) pythonClassName##_richCompare
#define PYTHON_NO_RICH_COMPARE 0
// default as_ table names
#define PYTHON_DEFAULT_AS_NUMBER(pythonClassName) &pythonClassName##_as_number
#define PYTHON_NO_AS_NUMBER 0
#define PYTHON_DEFAULT_AS_SEQUENCE(pythonClassName) &pythonClassName##_as_sequence
#define PYTHON_NO_AS_SEQUENCE 0
#define PYTHON_DEFAULT_AS_MAPPING(pythonClassName) &pythonClassName##_as_mapping
#define PYTHON_NO_AS_MAPPING 0
// str function
#define PYTHON_DEFAULT_STR(pythonClassName) (reprfunc)pythonClassName##_str
#define PYTHON_NO_STR 0
// get/set table default name
#define PYTHON_DEFAULT_GETSET(pythonClassName) pythonClassName##_getseters
#define PYTHON_NO_GETSET 0
// default base pointer
#define PYTHON_DEFAULT_BASE_TYPE(glueBaseClass) glueBaseClass::type_ptr
#define PYTHON_NO_BASE 0
// for glue functions that need custom stuff, you need to define the macros yourself
#define PLASMA_CUSTOM_TYPE(pythonClassName, docString) \
PYTHON_TYPE_START(pythonClassName) \
0, /* ob_size */ \
"Plasma." #pythonClassName, /* tp_name */ \
sizeof(pythonClassName), /* tp_basicsize */ \
0, /* tp_itemsize */ \
PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \
0, /* tp_print */ \
0, /* tp_getattr */ \
0, /* tp_setattr */ \
pythonClassName##_COMPARE, /* tp_compare */ \
0, /* tp_repr */ \
pythonClassName##_AS_NUMBER, /* tp_as_number */ \
pythonClassName##_AS_SEQUENCE, /* tp_as_sequence */ \
pythonClassName##_AS_MAPPING, /* tp_as_mapping */ \
0, /* tp_hash */ \
0, /* tp_call */ \
pythonClassName##_STR, /* tp_str */ \
0, /* tp_getattro */ \
0, /* tp_setattro */ \
0, /* tp_as_buffer */ \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \
docString, /* tp_doc */ \
0, /* tp_traverse */ \
0, /* tp_clear */ \
pythonClassName##_RICH_COMPARE, /* tp_richcompare */ \
0, /* tp_weaklistoffset */ \
0, /* tp_iter */ \
0, /* tp_iternext */ \
PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \
0, /* tp_members */ \
pythonClassName##_GETSET, /* tp_getset */ \
pythonClassName##_BASE, /* tp_base */ \
0, /* tp_dict */ \
0, /* tp_descr_get */ \
0, /* tp_descr_set */ \
0, /* tp_dictoffset */ \
PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \
0, /* tp_alloc */ \
PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \
PYTHON_TYPE_END
// for conviencence when we just need a base class
#define PLASMA_DEFAULT_TYPE_WBASE(pythonClassName, glueBaseClass, docString) \
PYTHON_TYPE_START(pythonClassName) \
0, /* ob_size */ \
"Plasma." #pythonClassName, /* tp_name */ \
sizeof(pythonClassName), /* tp_basicsize */ \
0, /* tp_itemsize */ \
PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \
0, /* tp_print */ \
0, /* tp_getattr */ \
0, /* tp_setattr */ \
0, /* tp_compare */ \
0, /* tp_repr */ \
0, /* tp_as_number */ \
0, /* tp_as_sequence */ \
0, /* tp_as_mapping */ \
0, /* tp_hash */ \
0, /* tp_call */ \
0, /* tp_str */ \
0, /* tp_getattro */ \
0, /* tp_setattro */ \
0, /* tp_as_buffer */ \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \
docString, /* tp_doc */ \
0, /* tp_traverse */ \
0, /* tp_clear */ \
0, /* tp_richcompare */ \
0, /* tp_weaklistoffset */ \
0, /* tp_iter */ \
0, /* tp_iternext */ \
PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \
0, /* tp_members */ \
0, /* tp_getset */ \
glueBaseClass::type_ptr, /* tp_base */ \
0, /* tp_dict */ \
0, /* tp_descr_get */ \
0, /* tp_descr_set */ \
0, /* tp_dictoffset */ \
PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \
0, /* tp_alloc */ \
PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \
PYTHON_TYPE_END
// small macros so that the type object can be accessed outside the glue file (for subclassing)
#define PYTHON_EXPOSE_TYPE static PyTypeObject* type_ptr
#define PYTHON_EXPOSE_TYPE_DEFINITION(pythonClass, glueClass) PyTypeObject* glueClass::type_ptr = &pythonClass##_type
/////////////////////////////////////////////////////////////////////
// Python class import macros
/////////////////////////////////////////////////////////////////////
// called at the beginning of the class definition function to grab the module
#define PYTHON_CLASS_IMPORT_START(m) \
if (m == NULL) \
return; \
\
Py_INCREF(m)
// called at the end of the class definition function to release the module
#define PYTHON_CLASS_IMPORT_END(m) Py_DECREF(m)
// called for each class you want to add to the module
#define PYTHON_CLASS_IMPORT(m, pythonClassName) \
if (PyType_Ready(&pythonClassName##_type) < 0) \
return; \
\
Py_INCREF(&pythonClassName##_type); \
PyModule_AddObject(m, #pythonClassName, (PyObject*)&pythonClassName##_type)
/////////////////////////////////////////////////////////////////////
// Python method macros
/////////////////////////////////////////////////////////////////////
// Handles the three types of methods python uses
#define PYTHON_METHOD_DEFINITION(pythonClassName, methodName, argsVar) \
static PyObject *pythonClassName##_##methodName(pythonClassName *self, PyObject *argsVar)
#define PYTHON_METHOD_DEFINITION_NOARGS(pythonClassName, methodName) \
static PyObject *pythonClassName##_##methodName(pythonClassName *self)
#define PYTHON_METHOD_DEFINITION_STATIC(pythonClassName, methodName, argsVar) \
static PyObject *pythonClassName##_##methodName(PyObject*, PyObject *argsVar)
#define PYTHON_METHOD_DEFINITION_STATIC_WKEY(pythonClassName, methodName, argsVar, keywordsVar) \
static PyObject *pythonClassName##_##methodName(PyObject*, PyObject *argsVar, PyObject *keywordsVar)
#define PYTHON_METHOD_DEFINITION_WKEY(pythonClassName, methodName, argsVar, keywordsVar) \
static PyObject *pythonClassName##_##methodName(pythonClassName *self, PyObject *argsVar, PyObject *keywordsVar)
// A very basic function, just calls the class method and returns None
#define PYTHON_BASIC_METHOD_DEFINITION(pythonClassName, methodName, classMethodName) \
static PyObject *pythonClassName##_##methodName(pythonClassName *self) \
{ \
self->fThis->classMethodName(); \
PYTHON_RETURN_NONE; \
}
// Different basic return types
#define PYTHON_RETURN_ERROR {return NULL;}
#define PYTHON_RETURN_NONE {Py_INCREF(Py_None); return Py_None;}
#define PYTHON_RETURN_BOOL(testValue) \
{ \
if (testValue) \
return PyInt_FromLong((long)1); \
else \
return PyInt_FromLong((long)0); \
}
#define PYTHON_RETURN_NOT_IMPLEMENTED {Py_INCREF(Py_NotImplemented); return Py_NotImplemented;}
// method table start
#define PYTHON_START_METHODS_TABLE(pythonClassName) static PyMethodDef pythonClassName##_methods[] = {
// method table end (automatically adds sentinal value)
#define PYTHON_END_METHODS_TABLE {NULL} }
// basic method
#define PYTHON_METHOD(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_VARARGS, docString}
// method with no arguments
#define PYTHON_METHOD_NOARGS(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_NOARGS, docString}
// static method with arguments
#define PYTHON_METHOD_STATIC(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_STATIC | METH_VARARGS, docString}
// static method with keywords
#define PYTHON_METHOD_STATIC_WKEY(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_STATIC | METH_VARARGS | METH_KEYWORDS, docString}
// method with keywords
#define PYTHON_METHOD_WKEY(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_VARARGS | METH_KEYWORDS, docString}
// really basic method
#define PYTHON_BASIC_METHOD(pythonClassName, methodName, docString) \
{#methodName, (PyCFunction)pythonClassName##_##methodName, METH_NOARGS, docString}
/////////////////////////////////////////////////////////////////////
// Get/set macros
/////////////////////////////////////////////////////////////////////
// setter defines
#define PYTHON_RETURN_SET_ERROR return -1;
#define PYTHON_RETURN_SET_OK return 0;
// getter function definition
#define PYTHON_GET_DEFINITION(pythonClassName, attribName) \
static PyObject *pythonClassName##_get##attribName(pythonClassName *self, void *closure)
// setter function definition
#define PYTHON_SET_DEFINITION(pythonClassName, attribName, valueVarName) \
static int pythonClassName##_set##attribName(pythonClassName *self, PyObject *valueVarName, void *closure)
// read-only setter function
#define PYTHON_SET_DEFINITION_READONLY(pythonClassName, attribName) \
int pythonClassName##_set##attribName(PyObject *self, PyObject *value, void *closure) \
{ \
PyErr_SetString(PyExc_RuntimeError, #attribName " is read-only"); \
PYTHON_RETURN_SET_ERROR; \
}
// starts off the get/set table
#define PYTHON_START_GETSET_TABLE(pythonClassName) static PyGetSetDef pythonClassName##_getseters[] = {
// and easy terminator to make things look pretty (automatically adds sentinal value)
#define PYTHON_END_GETSET_TABLE {NULL} }
// the get/set definition
#define PYTHON_GETSET(pythonClassName, attribName, docString) {#attribName, \
(getter)pythonClassName##_get##attribName, (setter)pythonClassName##_set##attribName, \
docString, NULL}
/////////////////////////////////////////////////////////////////////
// as_ table macros
/////////////////////////////////////////////////////////////////////
// as_number table
#define PYTHON_START_AS_NUMBER_TABLE(pythonClassName) static PyNumberMethods pythonClassName##_as_number = {
#define PYTHON_END_AS_NUMBER_TABLE }
// as_sequence table
#define PYTHON_START_AS_SEQUENCE_TABLE(pythonClassName) static PySequenceMethods pythonClassName##_as_sequence = {
#define PYTHON_END_AS_SEQUENCE_TABLE }
// as_mapping table
#define PYTHON_START_AS_MAPPING_TABLE(pythonClassName) static PyMappingMethods pythonClassName##_as_mapping = {
#define PYTHON_END_AS_MAPPING_TABLE }
/////////////////////////////////////////////////////////////////////
// Compare/Rich compare functions
/////////////////////////////////////////////////////////////////////
// compare
#define PYTHON_COMPARE_DEFINITION(pythonClassName, obj1, obj2) int pythonClassName##_compare(PyObject *obj1, PyObject *obj2)
#define PYTHON_COMPARE_LESS_THAN return -1
#define PYTHON_COMPARE_GREATER_THAN return 1
#define PYTHON_COMPARE_EQUAL return 0
// rich compare
#define PYTHON_RICH_COMPARE_DEFINITION(pythonClassName, obj1, obj2, compareType) PyObject *pythonClassName##_richCompare(PyObject *obj1, PyObject *obj2, int compareType)
#define PYTHON_RCOMPARE_TRUE return PyInt_FromLong((long)1)
#define PYTHON_RCOMPARE_FALSE return PyInt_FromLong((long)0)
#define PYTHON_RCOMPARE_ERROR return PyInt_FromLong((long)-1)
/////////////////////////////////////////////////////////////////////
// str functions
/////////////////////////////////////////////////////////////////////
// str function
#define PYTHON_STR_DEFINITION(pythonClassName) PyObject *pythonClassName##_str(pythonClassName *self)
/////////////////////////////////////////////////////////////////////
// Global function macros (for those functions that aren't in a class)
/////////////////////////////////////////////////////////////////////
// Global method
#define PYTHON_GLOBAL_METHOD_DEFINITION(methodName, argsVar, docString) \
static PyObject *methodName(PyObject*, PyObject*); /* forward declaration so struct can go here */ \
static PyMethodDef methodName##_method = {#methodName, methodName, METH_VARARGS, docString}; \
static PyObject *methodName(PyObject *self, PyObject *argsVar) /* and now for the actual function */
// Global method with no arguments
#define PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(methodName, docString) \
static PyObject *methodName(PyObject*); /* forward declaration so struct can go here */ \
static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_NOARGS, docString}; \
static PyObject *methodName(PyObject *self) /* and now for the actual function */
// Global method with keywords
#define PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(methodName, argsVar, keywordsVar, docString) \
static PyObject *methodName(PyObject*, PyObject*, PyObject*); /* forward declaration so struct can go here */ \
static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_VARARGS | METH_KEYWORDS, docString}; \
static PyObject *methodName(PyObject *self, PyObject *argsVar, PyObject *keywordsVar) /* and now for the actual function */
// Basic global method
#define PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(methodName, classMethodName, docString) \
static PyObject *methodName(PyObject*); /* forward declaration so struct can go here */ \
static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_NOARGS, docString}; \
static PyObject *methodName(PyObject *self) /* and now for the actual function */ \
{ \
classMethodName(); \
PYTHON_RETURN_NONE; \
}
// this goes in the definition function
#define PYTHON_GLOBAL_METHOD(vectorVarName, methodName) vectorVarName.push_back(methodName##_method)
// not necessary, but for continuity with the NOARGS function definition above
#define PYTHON_GLOBAL_METHOD_NOARGS(vectorVarName, methodName) vectorVarName.push_back(methodName##_method);
// not necessary, but for continuity with the WKEY function definition above
#define PYTHON_GLOBAL_METHOD_WKEY(vectorVarName, methodName) vectorVarName.push_back(methodName##_method)
// not necessary, but for continuity with the BASIC function definition above
#define PYTHON_BASIC_GLOBAL_METHOD(vectorVarName, methodName) vectorVarName.push_back(methodName##_method)
/////////////////////////////////////////////////////////////////////
// Enum glue (these should all be inside a function)
/////////////////////////////////////////////////////////////////////
// the start of an enum block
#define PYTHON_ENUM_START(enumName) std::map<std::string, int> enumName##_enumValues
// for each element of the enum
#define PYTHON_ENUM_ELEMENT(enumName, elementName, elementValue) enumName##_enumValues[#elementName] = elementValue
// to finish off and define the enum
#define PYTHON_ENUM_END(m, enumName) pyEnum::MakeEnum(m, #enumName, enumName##_enumValues)
#endif // _pyGlueHelpers_h_