Salome HOME
Copyright update 2022
[modules/kernel.git] / src / SALOMESDS / SALOMESDS_PickelizedPyObjServer.cxx
1 // Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Anthony GEAY (EDF R&D)
20
21 #include "SALOMESDS_PickelizedPyObjServer.hxx"
22 #include "SALOMESDS_DataScopeServer.hxx"
23 #include "SALOMESDS_Exception.hxx"
24
25 #include <iostream>
26 #include <sstream>
27
28 using namespace SALOMESDS;
29
30 PickelizedPyObjServer::PickelizedPyObjServer(DataScopeServerBase *father, const std::string& varName, const SALOME::ByteVec& value):BasicDataServer(father,varName),_self(0)
31 {
32   setSerializedContentInternal(value);
33 }
34
35 //! obj is consumed
36 PickelizedPyObjServer::PickelizedPyObjServer(DataScopeServerBase *father, const std::string& varName, PyObject *obj):BasicDataServer(father,varName),_self(0)
37 {
38   setNewPyObj(obj);
39 }
40
41 PickelizedPyObjServer::~PickelizedPyObjServer()
42 {
43   Py_XDECREF(_self);
44 }
45
46 /*!
47  * Called remotely -> to protect against throw
48  */
49 SALOME::ByteVec *PickelizedPyObjServer::fetchSerializedContent()
50 {
51   Py_XINCREF(_self);//because pickelize consume _self
52   return FromCppToByteSeq(pickelize(_self));
53 }
54
55 bool PickelizedPyObjServer::isDict()
56 {
57   if(PyDict_Check(_self))
58     return true;
59   else
60     return false;
61 }
62
63 void PickelizedPyObjServer::checkKeyNotAlreadyPresent(PyObject *key)
64 {
65   checkKeyPresence(key,false);
66 }
67
68 void PickelizedPyObjServer::checkKeyPresent(PyObject *key)
69 {
70   checkKeyPresence(key,true);
71 }
72
73 void PickelizedPyObjServer::addKeyValueHard(PyObject * /*key*/, PyObject * /*value*/)
74 {
75   std::ostringstream oss; oss << "PickelizedPyObjServer::addKeyValueHard : var \"" << getVarNameCpp() << "\" is not permitted to alter its value !";
76   throw Exception(oss.str());
77 }
78
79 void PickelizedPyObjServer::removeKeyInVarErrorIfNotAlreadyExisting(PyObject * /*key*/)
80 {
81   std::ostringstream oss; oss << "PickelizedPyObjServer::removeKeyInVarErrorIfNotAlreadyExisting : var \"" << getVarNameCpp() << "\" is not permitted to alter its value !";
82   throw Exception(oss.str());
83 }
84
85 void PickelizedPyObjServer::FromByteSeqToCpp(const SALOME::ByteVec& bsToBeConv, std::string& ret)
86 {
87   std::size_t sz(bsToBeConv.length());
88   ret.resize(sz,' ');
89   char *buf(const_cast<char *>(ret.c_str()));
90   for(std::size_t i=0;i<sz;i++)
91     buf[i]=bsToBeConv[(CORBA::ULong)i]; //!< TODO: size_t to CORBA::ULong
92 }
93
94 void PickelizedPyObjServer::FromCppToByteSeq(const std::string& strToBeConv, SALOME::ByteVec& ret)
95 {
96   const char *buf(strToBeConv.c_str());
97   std::size_t sz(strToBeConv.size());
98   ret.length((CORBA::ULong)sz); //!< TODO: size_t to CORBA::ULong
99   for(std::size_t i=0;i<sz;i++)
100     ret[(CORBA::ULong)i]=buf[i]; //!< TODO: size_t to CORBA::ULong
101 }
102
103 SALOME::ByteVec *PickelizedPyObjServer::FromCppToByteSeq(const std::string& strToBeConv)
104 {
105   SALOME::ByteVec *ret(new SALOME::ByteVec);
106   FromCppToByteSeq(strToBeConv,*ret);
107   return ret;
108 }
109
110 //! New reference returned
111 PyObject *PickelizedPyObjServer::GetPyObjFromPickled(const std::string& pickledData, DataScopeServerBase *dsb)
112 {
113   std::size_t sz(pickledData.size());
114   PyObject *pickledDataPy(PyBytes_FromStringAndSize(NULL,sz));// agy : do not use PyUnicode_FromString because std::string hides a vector of byte.
115   char *buf(PyBytes_AS_STRING(pickledDataPy));// this buf can be used thanks to python documentation.
116   const char *inBuf(pickledData.c_str());
117   std::copy(inBuf,inBuf+sz,buf);
118   PyObject *selfMeth(PyObject_GetAttrString(dsb->getPickler(),"loads"));
119   PyObject *args(PyTuple_New(1)); PyTuple_SetItem(args,0,pickledDataPy);
120   PyObject *ret(PyObject_CallObject(selfMeth,args));
121   Py_XDECREF(args);
122   Py_XDECREF(selfMeth);
123   return ret;
124 }
125
126 //! New reference returned
127 PyObject *PickelizedPyObjServer::getPyObjFromPickled(const std::string& pickledData)
128 {
129   return GetPyObjFromPickled(pickledData,_father);
130 }
131
132 //! New reference returned
133 PyObject *PickelizedPyObjServer::GetPyObjFromPickled(const std::vector<unsigned char>& pickledData, DataScopeServerBase *dsb)
134 {
135   std::size_t sz(pickledData.size());
136   PyObject *pickledDataPy(PyBytes_FromStringAndSize(NULL,sz));// agy : do not use PyUnicode_FromString because std::string hides a vector of byte.
137   char *buf(PyBytes_AS_STRING(pickledDataPy));// this buf can be used thanks to python documentation.
138   const unsigned char *inBuf(pickledData.data());
139   std::copy(inBuf,inBuf+sz,buf);
140   PyObject *selfMeth(PyObject_GetAttrString(dsb->getPickler(),"loads"));
141   PyObject *args(PyTuple_New(1)); PyTuple_SetItem(args,0,pickledDataPy);
142   PyObject *ret(PyObject_CallObject(selfMeth,args));
143   Py_XDECREF(args);
144   Py_XDECREF(selfMeth);
145   return ret;
146 }
147
148 //! New reference returned
149 PyObject *PickelizedPyObjServer::getPyObjFromPickled(const std::vector<unsigned char>& pickledData)
150 {
151   return GetPyObjFromPickled(pickledData,_father);
152 }
153
154 //! obj is consumed by this method.
155 std::string PickelizedPyObjServer::Pickelize(PyObject *obj, DataScopeServerBase *dsb)
156 {
157   PyObject *args(PyTuple_New(2));
158   PyTuple_SetItem(args,0,obj);
159   PyTuple_SetItem(args,1,PyLong_FromLong(3));// because "assert(pickle.HIGHEST_PROTOCOL is 3)"
160   PyObject *selfMeth(PyObject_GetAttrString(dsb->getPickler(),"dumps"));
161   PyObject *retPy(PyObject_CallObject(selfMeth,args));
162   Py_XDECREF(selfMeth);
163   Py_XDECREF(args);
164   std::size_t sz(PyBytes_Size(retPy));
165   std::string ret(sz,'\0');
166   const char *buf(PyBytes_AS_STRING(retPy));
167   char *inBuf(const_cast<char *>(ret.c_str()));
168   for(std::size_t i=0;i<sz;i++)
169     inBuf[i]=buf[i];
170   Py_XDECREF(retPy);
171   return ret;
172 }
173
174 //! obj is consumed by this method.
175 std::string PickelizedPyObjServer::pickelize(PyObject *obj)
176 {
177   return Pickelize(obj,_father);
178 }
179
180 //! obj is consumed by this method.
181 void PickelizedPyObjServer::setNewPyObj(PyObject *obj)
182 {
183   if(!obj)
184     throw Exception("PickelizedPyObjServer::setNewPyObj : trying to assign a NULL pyobject in this !");
185   if(obj==_self)
186     return ;
187   if(PyList_Check(obj)==0 && PyDict_Check(obj)==0 && PyTuple_Check(obj)==0 && PyBytes_Check(obj)==0 && PyLong_Check(obj)==0 && PyBool_Check(obj)==0 && PyFloat_Check(obj)==0 && obj!=Py_None)
188     throw Exception("PickelizedPyObjServer::setNewPyObj : Supported python types are [list,tuple,dict,bytes,int,bool,float,None] !");
189   if(_self)
190     {
191       PyObject *selfType(PyObject_Type(_self));
192       if(PyObject_IsInstance(obj,selfType)!=1)
193         {
194           Py_XDECREF(obj);
195           Py_XDECREF(selfType);
196           throw Exception("PickelizedPyObjServer::setNewPyObj : type of new object is not the same than those previously set !");
197         }
198       else
199         Py_XDECREF(selfType);
200     }
201   Py_XDECREF(_self);
202   _self=obj;
203 }
204
205 void PickelizedPyObjServer::setSerializedContentInternal(const SALOME::ByteVec& newValue)
206 {
207   std::string data;
208   FromByteSeqToCpp(newValue,data);
209   setNewPyObj(getPyObjFromPickled(data));
210 }
211
212 PyObject *PickelizedPyObjServer::CreateDftObjFromType(PyObject *globals, const std::string& typeName)
213 {
214   PyObject *builtins(PyDict_GetItemString(globals,"__builtins__"));
215   if(!builtins)
216     throw Exception("PickelizedPyObjServer constructor : no __builtins__ in globals !");
217   PyObject *builtins2(PyModule_GetDict(builtins));
218   if(!builtins2)
219     throw Exception("PickelizedPyObjServer constructor : internal error fail to invoke __dict__ on __builtins__ !");
220   PyObject *tmp(PyDict_GetItemString(builtins2,typeName.c_str()));
221   if(!tmp)
222     {
223       std::ostringstream oss; oss << "PickelizedPyObjServer::CreateDftObjFromType : Invalid type name \"" << typeName << "\" !";
224       throw Exception(oss.str());
225     }
226   PyObject *args(PyTuple_New(0));
227   PyObject *ret(PyObject_CallObject(tmp,args));
228   Py_XDECREF(args);
229   return ret;
230 }
231
232 void PickelizedPyObjServer::checkKeyPresence(PyObject *key, bool presence)
233 {
234   if(!isDict())
235     throw Exception("PickelizedPyObjServer::checkKeyPresence : not a dict !");
236   PyObject *selfMeth(PyObject_GetAttrString(_self,"__contains__"));//new ref
237   PyObject *args(PyTuple_New(1));
238   PyTuple_SetItem(args,0,key); Py_XINCREF(key);// key is stolen by PyTuple_SetItem
239   PyObject *retPy(PyObject_CallObject(selfMeth,args));
240   Py_XDECREF(args);
241   Py_XDECREF(selfMeth);
242   //
243   if(retPy!=Py_False && retPy!=Py_True)
244     throw Exception("PickelizedPyObjServer::checkKeyPresence : unexpected return of dict.__contains__ !");
245   if(!presence)
246     {
247       if(retPy==Py_True)
248         throw Exception("PickelizedPyObjServer::checkKeyPresence : key is already present and it should not !");
249     }
250   else
251     {
252       if(retPy==Py_False)
253         throw Exception("PickelizedPyObjServer::checkKeyPresence : key is not present and it should !");
254     }
255   Py_XDECREF(retPy);
256 }
257
258 PickelizedPyObjServerModifiable::PickelizedPyObjServerModifiable(DataScopeServerBase *father, const std::string& varName, const SALOME::ByteVec& value):PickelizedPyObjServer(father,varName,value)
259 {
260 }
261
262 PickelizedPyObjServerModifiable::PickelizedPyObjServerModifiable(DataScopeServerBase *father, const std::string& varName, PyObject *obj):PickelizedPyObjServer(father,varName,obj)
263 {
264 }
265
266 void PickelizedPyObjServerModifiable::addKeyValueErrorIfAlreadyExisting(PyObject *key, PyObject *value)
267 {
268   checkKeyNotAlreadyPresent(key);
269   bool isOK(PyDict_SetItem(_self,key,value)==0);
270   if(!isOK)
271     throw Exception("PickelizedPyObjServerModifiable::addKeyValueErrorIfAlreadyExisting : error when trying to add key,value to dict !");
272 }
273
274 void PickelizedPyObjServerModifiable::removeKeyInVarErrorIfNotAlreadyExisting(PyObject *key)
275 {
276   checkKeyPresent(key);
277   if(PyDict_DelItem(_self,key)!=0)
278     throw Exception("PickelizedPyObjServerModifiable::removeKeyInVarErrorIfNotAlreadyExisting : error during deletion of key in dict !");
279 }