From fd2b1156783d52dbb7c93c53fe008d9e14cbffdd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Apr 2009 14:51:06 +0000 Subject: Python BGE API - Initialize python types with PyType_Ready, which adds methods to the type dictionary. - use Pythons get/setattro (uses a python string for the attribute rather then char*). Using basic C strings seems nice but internally python converts them to python strings and discards them for most functions that accept char arrays. - Method lookups use the PyTypes dictionary (should be faster then Py_FindMethod) - Renamed __getattr -> py_base_getattro, _getattr -> py_getattro, __repr -> py_base_repr, py_delattro, py_getattro_self etc. From here is possible to put all the parent classes methods into each python types dictionary to avoid nested lookups (api has 4 levels of lookups in some places), tested this but its not ready yet. Simple tests for getting a method within a loop show this to be between 0.5 and 3.2x faster then using Py_FindMethod() --- source/gameengine/Expressions/ListValue.cpp | 17 ++++---- source/gameengine/Expressions/ListValue.h | 2 +- source/gameengine/Expressions/PyObjectPlus.cpp | 54 +++++++++++++++----------- source/gameengine/Expressions/PyObjectPlus.h | 50 ++++++++++++++---------- source/gameengine/Expressions/Value.cpp | 36 ++++++++++------- source/gameengine/Expressions/Value.h | 8 ++-- 6 files changed, 96 insertions(+), 71 deletions(-) (limited to 'source/gameengine/Expressions') diff --git a/source/gameengine/Expressions/ListValue.cpp b/source/gameengine/Expressions/ListValue.cpp index aa84cab0266..15eb8835b79 100644 --- a/source/gameengine/Expressions/ListValue.cpp +++ b/source/gameengine/Expressions/ListValue.cpp @@ -193,7 +193,7 @@ static PyMappingMethods instance_as_mapping = { PyTypeObject CListValue::Type = { - PyObject_HEAD_INIT(&PyType_Type) + PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "CListValue", /*tp_name*/ sizeof(CListValue), /*tp_basicsize*/ @@ -201,16 +201,19 @@ PyTypeObject CListValue::Type = { /* methods */ PyDestructor, /*tp_dealloc*/ 0, /*tp_print*/ - __getattr, /*tp_getattr*/ - __setattr, /*tp_setattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ 0, /*tp_compare*/ - __repr, /*tp_repr*/ + py_base_repr, /*tp_repr*/ 0, /*tp_as_number*/ &listvalue_as_sequence, /*tp_as_sequence*/ &instance_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call */ - 0,0,0,0,0,0,0,0,0,0,0,0, + 0, + py_base_getattro, + py_base_setattro, + 0,0,0,0,0,0,0,0,0, Methods }; @@ -238,8 +241,8 @@ PyAttributeDef CListValue::Attributes[] = { { NULL } //Sentinel }; -PyObject* CListValue::_getattr(const char *attr) { - _getattr_up(CValue); +PyObject* CListValue::py_getattro(PyObject* attr) { + py_getattro_up(CValue); } diff --git a/source/gameengine/Expressions/ListValue.h b/source/gameengine/Expressions/ListValue.h index 104e3e63283..f936298a8c4 100644 --- a/source/gameengine/Expressions/ListValue.h +++ b/source/gameengine/Expressions/ListValue.h @@ -59,7 +59,7 @@ public: bool CheckEqual(CValue* first,CValue* second); - virtual PyObject* _getattr(const char *attr); + virtual PyObject* py_getattro(PyObject* attr); KX_PYMETHOD_O(CListValue,append); KX_PYMETHOD_NOARGS(CListValue,reverse); diff --git a/source/gameengine/Expressions/PyObjectPlus.cpp b/source/gameengine/Expressions/PyObjectPlus.cpp index 417388be464..ed6932b414a 100644 --- a/source/gameengine/Expressions/PyObjectPlus.cpp +++ b/source/gameengine/Expressions/PyObjectPlus.cpp @@ -55,19 +55,22 @@ ------------------------------*/ PyTypeObject PyObjectPlus::Type = { - PyObject_HEAD_INIT(&PyType_Type) + PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "PyObjectPlus", /*tp_name*/ sizeof(PyObjectPlus), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ - PyDestructor, /*tp_dealloc*/ - 0, /*tp_print*/ - __getattr, /*tp_getattr*/ - __setattr, /*tp_setattr*/ - 0, /*tp_compare*/ - __repr, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + PyDestructor, + 0, + 0, + 0, + 0, + py_base_repr, + 0,0,0,0,0,0, + py_base_getattro, + py_base_setattro, + 0,0,0,0,0,0,0,0,0, Methods }; @@ -103,37 +106,41 @@ PyParentObject PyObjectPlus::Parents[] = {&PyObjectPlus::Type, NULL}; /*------------------------------ * PyObjectPlus attributes -- attributes ------------------------------*/ -PyObject *PyObjectPlus::_getattr(const char *attr) +PyObject *PyObjectPlus::py_getattro(PyObject* attr) { - if (!strcmp(attr, "__doc__") && GetType()->tp_doc) - return PyString_FromString(GetType()->tp_doc); - + PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \ + if (descr == NULL) { + PyErr_SetString(PyExc_AttributeError, "attribute not found"); + return NULL; + } else { + return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, (PyObject *)this); \ + } //if (streq(attr, "type")) // return Py_BuildValue("s", (*(GetParents()))->tp_name); - - return Py_FindMethod(Methods, this, attr); } -int PyObjectPlus::_delattr(const char *attr) +int PyObjectPlus::py_delattro(PyObject* attr) { PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted"); return 1; } -int PyObjectPlus::_setattr(const char *attr, PyObject *value) +int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value) { - //return PyObject::_setattr(attr,value); + //return PyObject::py_setattro(attr,value); //cerr << "Unknown attribute" << endl; PyErr_SetString(PyExc_AttributeError, "attribute cant be set"); return 1; } -PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *self, const char *attr) +PyObject *PyObjectPlus::py_getattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr) { + char *attr_str= PyString_AsString(attr); + const PyAttributeDef *attrdef; for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++) { - if (!strcmp(attr, attrdef->m_name)) + if (!strcmp(attr_str, attrdef->m_name)) { if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY) { @@ -242,16 +249,17 @@ PyObject *PyObjectPlus::_getattr_self(const PyAttributeDef attrlist[], void *sel return NULL; } -int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, const char *attr, PyObject *value) +int PyObjectPlus::py_setattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr, PyObject *value) { const PyAttributeDef *attrdef; void *undoBuffer = NULL; void *sourceBuffer = NULL; size_t bufferSize = 0; + char *attr_str= PyString_AsString(attr); for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++) { - if (!strcmp(attr, attrdef->m_name)) + if (!strcmp(attr_str, attrdef->m_name)) { if (attrdef->m_access == KX_PYATTRIBUTE_RO || attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY) @@ -684,7 +692,7 @@ int PyObjectPlus::_setattr_self(const PyAttributeDef attrlist[], void *self, con /*------------------------------ * PyObjectPlus repr -- representations ------------------------------*/ -PyObject *PyObjectPlus::_repr(void) +PyObject *PyObjectPlus::py_repr(void) { PyErr_SetString(PyExc_SystemError, "Representation not overridden by object."); return NULL; @@ -726,7 +734,7 @@ PyObject *PyObjectPlus::Py_isA(PyObject *value) // Python wrapper for isA Py_RETURN_FALSE; } -/* Utility function called by the macro _getattr_up() +/* Utility function called by the macro py_getattro_up() * for getting ob.__dict__() values from our PyObject * this is used by python for doing dir() on an object, so its good * if we return a list of attributes and methods. diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h index 345eb8c9c3f..77963c092eb 100644 --- a/source/gameengine/Expressions/PyObjectPlus.h +++ b/source/gameengine/Expressions/PyObjectPlus.h @@ -72,6 +72,8 @@ typedef int Py_ssize_t; #define PY_METHODCHAR const char * #endif +#include "descrobject.h" + static inline void Py_Fatal(const char *M) { fprintf(stderr, "%s\n", M); exit(-1); @@ -90,21 +92,27 @@ static inline void Py_Fatal(const char *M) { - // This defines the _getattr_up macro + // This defines the py_getattro_up macro // which allows attribute and method calls // to be properly passed up the hierarchy. -#define _getattr_up(Parent) \ - PyObject *rvalue = Py_FindMethod(Methods, this, attr); \ + +#define py_getattro_up(Parent) \ + PyObject *rvalue; \ + PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \ \ - if (rvalue == NULL) { \ + if (descr == NULL) { \ PyErr_Clear(); \ - rvalue = Parent::_getattr(attr); \ + rvalue = Parent::py_getattro(attr); \ + } else { \ + rvalue= PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, (PyObject *)this); \ } \ - if (strcmp(attr, "__dict__")==0) {\ + \ + if (strcmp(PyString_AsString(attr), "__dict__")==0) {\ rvalue = _getattr_dict(rvalue, Methods, Attributes); \ } \ return rvalue; \ + /** * These macros are helpfull when embedding Python routines. The second * macro is one that also requires a documentation string @@ -361,29 +369,29 @@ public: // Py_DECREF(this); // }; // decref method - virtual PyObject *_getattr(const char *attr); // _getattr method - static PyObject *__getattr(PyObject * PyObj, char *attr) // This should be the entry in Type. + virtual PyObject *py_getattro(PyObject *attr); // py_getattro method + static PyObject *py_base_getattro(PyObject * PyObj, PyObject *attr) // This should be the entry in Type. { - return ((PyObjectPlus*) PyObj)->_getattr(attr); + return ((PyObjectPlus*) PyObj)->py_getattro(attr); } - static PyObject *_getattr_self(const PyAttributeDef attrlist[], void *self, const char *attr); - static int _setattr_self(const PyAttributeDef attrlist[], void *self, const char *attr, PyObject *value); + static PyObject *py_getattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr); + static int py_setattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr, PyObject *value); - virtual int _delattr(const char *attr); - virtual int _setattr(const char *attr, PyObject *value); // _setattr method - static int __setattr(PyObject *PyObj, // This should be the entry in Type. - char *attr, + virtual int py_delattro(PyObject *attr); + virtual int py_setattro(PyObject *attr, PyObject *value); // py_setattro method + static int py_base_setattro(PyObject *PyObj, // This should be the entry in Type. + PyObject *attr, PyObject *value) { - if (!value) - return ((PyObjectPlus*) PyObj)->_delattr(attr); - return ((PyObjectPlus*) PyObj)->_setattr(attr, value); + if (value==NULL) + return ((PyObjectPlus*) PyObj)->py_delattro(attr); + return ((PyObjectPlus*) PyObj)->py_setattro(attr, value); } - virtual PyObject *_repr(void); // _repr method - static PyObject *__repr(PyObject *PyObj) // This should be the entry in Type. + virtual PyObject *py_repr(void); // py_repr method + static PyObject *py_base_repr(PyObject *PyObj) // This should be the entry in Type. { - return ((PyObjectPlus*) PyObj)->_repr(); + return ((PyObjectPlus*) PyObj)->py_repr(); } // isA methods diff --git a/source/gameengine/Expressions/Value.cpp b/source/gameengine/Expressions/Value.cpp index 36509763454..8b910b9038b 100644 --- a/source/gameengine/Expressions/Value.cpp +++ b/source/gameengine/Expressions/Value.cpp @@ -139,19 +139,22 @@ static PyNumberMethods cvalue_as_number = { PyTypeObject CValue::Type = { - PyObject_HEAD_INIT(&PyType_Type) + PyObject_HEAD_INIT(NULL) 0, "CValue", sizeof(CValue), 0, PyDestructor, 0, - __getattr, - __setattr, + 0, + 0, &MyPyCompare, - __repr, + py_base_repr, &cvalue_as_number, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0, + py_base_getattro, + py_base_setattro, + 0,0,0,0,0,0,0,0,0, Methods }; @@ -695,9 +698,10 @@ PyAttributeDef CValue::Attributes[] = { }; -PyObject* CValue::_getattr(const char *attr) +PyObject* CValue::py_getattro(PyObject *attr) { - CValue* resultattr = GetProperty(attr); + char *attr_str= PyString_AsString(attr); + CValue* resultattr = GetProperty(attr_str); if (resultattr) { PyObject* pyconvert = resultattr->ConvertValueToPython(); @@ -707,7 +711,7 @@ PyObject* CValue::_getattr(const char *attr) else return resultattr; // also check if it's already in pythoninterpreter! } - _getattr_up(PyObjectPlus); + py_getattro_up(PyObjectPlus); } CValue* CValue::ConvertPythonToValue(PyObject* pyobj) @@ -769,26 +773,28 @@ CValue* CValue::ConvertPythonToValue(PyObject* pyobj) } -int CValue::_delattr(const char *attr) +int CValue::py_delattro(PyObject *attr) { - if (RemoveProperty(STR_String(attr))) + char *attr_str= PyString_AsString(attr); + if (RemoveProperty(STR_String(attr_str))) return 0; - PyErr_Format(PyExc_AttributeError, "attribute \"%s\" dosnt exist", attr); + PyErr_Format(PyExc_AttributeError, "attribute \"%s\" dosnt exist", attr_str); return 1; } -int CValue::_setattr(const char *attr, PyObject* pyobj) +int CValue::py_setattro(PyObject *attr, PyObject* pyobj) { CValue* vallie = ConvertPythonToValue(pyobj); if (vallie) { - CValue* oldprop = GetProperty(attr); + char *attr_str= PyString_AsString(attr); + CValue* oldprop = GetProperty(attr_str); if (oldprop) oldprop->SetValue(vallie); else - SetProperty(attr, vallie); + SetProperty(attr_str, vallie); vallie->Release(); } else @@ -796,7 +802,7 @@ int CValue::_setattr(const char *attr, PyObject* pyobj) return 1; /* ConvertPythonToValue sets the error message */ } - //PyObjectPlus::_setattr(attr,value); + //PyObjectPlus::py_setattro(attr,value); return 0; }; diff --git a/source/gameengine/Expressions/Value.h b/source/gameengine/Expressions/Value.h index 4678ab1f0c2..7a2816a9778 100644 --- a/source/gameengine/Expressions/Value.h +++ b/source/gameengine/Expressions/Value.h @@ -217,14 +217,14 @@ public: CValue(PyTypeObject *T = &Type); //static PyObject* PyMake(PyObject*,PyObject*); - virtual PyObject *_repr(void) + virtual PyObject *py_repr(void) { return Py_BuildValue("s",(const char*)GetText()); } - virtual PyObject* _getattr(const char *attr); + virtual PyObject* py_getattro(PyObject *attr); void SpecialRelease() { @@ -251,8 +251,8 @@ public: virtual CValue* ConvertPythonToValue(PyObject* pyobj); - virtual int _delattr(const char *attr); - virtual int _setattr(const char *attr, PyObject* value); + virtual int py_delattro(PyObject *attr); + virtual int py_setattro(PyObject *attr, PyObject* value); virtual PyObject* ConvertKeysToPython( void ); -- cgit v1.2.3