![]() |
|
|
Generated: 24 Nov 2008 |
00001 00008 #include "GaudiPython/PyROOTPickle.h" 00009 #include "Python.h" 00010 #include "TClass.h" 00011 #include "TClassRef.h" 00012 #include "TBufferFile.h" 00013 #include "TPython.h" 00014 #include "RVersion.h" 00015 00016 //- data _______________________________________________________________________ 00017 #if ROOT_VERSION_CODE < ROOT_VERSION(5,19,0) 00018 static PyObject* gExpand = 0; 00019 #endif 00020 00021 namespace GaudiPython { 00022 00023 #if ROOT_VERSION_CODE < ROOT_VERSION(5,19,0) 00024 00029 PyObject* ObjectProxyReduce( PyObject* self ) 00030 { 00031 // Turn the object proxy instance into a character stream and return for 00032 // pickle, together with the callable object that can restore the stream 00033 // into the object proxy instance. 00034 00035 void* vself = TPython::ObjectProxy_AsVoidPtr( self ); // checks type 00036 if ( ! vself ) { 00037 PyErr_SetString( PyExc_TypeError, 00038 "__reduce__ requires an object proxy instance as first argument" ); 00039 return 0; 00040 } 00041 00042 PyObject* nattr = PyObject_GetAttrString( (PyObject*)self->ob_type, (char*)"__name__" ); 00043 PyObject* pyname = PyObject_Str( nattr ); 00044 Py_DECREF( nattr ); 00045 00046 static TClass* bufferclass = TClass::GetClass("TBufferFile"); 00047 TClass* klass = TClass::GetClass( PyString_AS_STRING( pyname ) ); 00048 00049 // no cast is needed, but WriteObject taking a TClass argument is protected, 00050 // so use WriteObjectAny() 00051 TBufferFile* buf = 0; 00052 if ( klass == bufferclass ) { 00053 buf = (TBufferFile*)vself; 00054 } 00055 else { 00056 static TBufferFile buffer( TBuffer::kWrite ); 00057 buffer.Reset(); 00058 if ( buffer.WriteObjectAny( vself, klass ) != 1 ) { 00059 PyErr_Format( PyExc_IOError, 00060 "could not stream object of type %s", PyString_AS_STRING( pyname ) ); 00061 Py_DECREF( pyname ); 00062 return 0; 00063 } 00064 buf = &buffer; 00065 } 00066 00067 // use a string for the serialized result, as a python buffer will not copy 00068 // the buffer contents; use a string for the class name, used when casting 00069 // on reading back in 00070 PyObject* res2 = PyTuple_New( 2 ); 00071 PyTuple_SET_ITEM( res2, 0, PyString_FromStringAndSize( buf->Buffer(), buf->Length() ) ); 00072 PyTuple_SET_ITEM( res2, 1, pyname ); 00073 00074 PyObject* result = PyTuple_New( 2 ); 00075 Py_INCREF( gExpand ); 00076 PyTuple_SET_ITEM( result, 0, gExpand ); 00077 PyTuple_SET_ITEM( result, 1, res2 ); 00078 00079 return result; 00080 } 00081 00082 00083 class ObjectProxy { 00084 public: 00085 enum EFlags { kNone = 0x0, kIsOwner = 0x0001, kIsReference = 0x0002 }; 00086 00087 public: 00088 void HoldOn() { fFlags |= kIsOwner; } 00089 void Release() { fFlags &= ~kIsOwner; } 00090 public: // public, as the python C-API works with C structs 00091 PyObject_HEAD 00092 void* fObject; 00093 TClassRef fClass; 00094 int fFlags; 00095 private: // private, as the python C-API will handle creation 00096 ObjectProxy() {} 00097 }; 00098 00099 00104 PyObject* ObjectProxyExpand( PyObject*, PyObject* args ) 00105 { 00106 // This method is a helper for (un)pickling of ObjectProxy instances. 00107 PyObject* pybuf = 0; 00108 const char* clname = 0; 00109 if ( ! PyArg_ParseTuple( args, const_cast< char* >( "O!s:__expand__" ), 00110 &PyString_Type, &pybuf, &clname ) ) 00111 return 0; 00112 00113 // use the PyString macro's to by-pass error checking; do not adopt the buffer, 00114 // as the local TBufferFile can go out of scope (there is no copying) 00115 void* result; 00116 if( strcmp(clname, "TBufferFile") == 0) { 00117 TBufferFile* buf = new TBufferFile( TBuffer::kWrite); 00118 buf->WriteFastArray( PyString_AS_STRING(pybuf), PyString_GET_SIZE( pybuf )); 00119 result = buf; 00120 } 00121 else { 00122 TBufferFile buf( TBuffer::kRead, 00123 PyString_GET_SIZE( pybuf ), PyString_AS_STRING( pybuf ), kFALSE ); 00124 result = buf.ReadObjectAny( 0 ); 00125 } 00126 PyObject* pobj = TPython::ObjectProxy_FromVoidPtr( result, clname ); 00127 // set Ownership of the returned object 00128 ObjectProxy* obj = (ObjectProxy*)pobj; 00129 obj->HoldOn(); 00130 return pobj; 00131 } 00132 00133 00139 void PyROOTPickle::Initialize( PyObject* libpyroot_pymodule, PyObject* objectproxy_pytype ) 00140 { 00141 Py_INCREF( libpyroot_pymodule ); 00142 PyTypeObject* pytype = (PyTypeObject*)objectproxy_pytype; 00143 00144 static PyMethodDef s_pdefExp = { (char*)"_ObjectProxy__expand__", 00145 (PyCFunction)ObjectProxyExpand, METH_VARARGS, (char*)"internal function" }; 00146 00147 PyObject* pymname = PyString_FromString( PyModule_GetName( libpyroot_pymodule ) ); 00148 gExpand = PyCFunction_NewEx( &s_pdefExp, NULL, pymname ); 00149 Py_DECREF( pymname ); 00150 Bool_t isOk = PyObject_SetAttrString( libpyroot_pymodule, s_pdefExp.ml_name, gExpand ) == 0; 00151 Py_DECREF( gExpand ); // is moderately risky, but Weakref not allowed (?) 00152 00153 if ( ! isOk ) { 00154 Py_DECREF( libpyroot_pymodule ); 00155 PyErr_SetString( PyExc_TypeError, "could not add expand function to libPyROOT" ); 00156 return; 00157 } 00158 00159 static PyMethodDef s_pdefRed = { (char*)"__reduce__", 00160 (PyCFunction)ObjectProxyReduce, METH_NOARGS, (char*)"internal function" }; 00161 00162 PyObject* descr = PyDescr_NewMethod( pytype, &s_pdefRed ); 00163 isOk = PyDict_SetItemString( pytype->tp_dict, s_pdefRed.ml_name, descr) == 0; 00164 Py_DECREF( descr ); 00165 if ( ! isOk ) { 00166 Py_DECREF( libpyroot_pymodule ); 00167 PyErr_SetString( PyExc_TypeError, "could not add __reduce__ function to ObjectProxy" ); 00168 return; 00169 } 00170 00171 Py_DECREF( libpyroot_pymodule ); 00172 } 00173 00174 #else // ROOT_VERSION_CODE < ROOT_VERSION(5,19,0) 00175 00176 void PyROOTPickle::Initialize( PyObject*, PyObject* ) 00177 { 00178 /* dummy. It is not needed for this version of ROOT */ 00179 } 00180 00181 #endif // ROOT_VERSION_CODE < ROOT_VERSION(5,19,0) 00182 00183 } // namespace GaudiPython