Salome HOME
Copyright update 2021
[modules/yacs.git] / src / runtime / DistributedPythonNode.cxx
1 // Copyright (C) 2006-2021  CEA/DEN, EDF R&D
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
20 #include "DistributedPythonNode.hxx"
21 #include "RuntimeSALOME.hxx"
22 #include "SalomeContainer.hxx"
23 #include "PythonNode.hxx"
24 #include "SalomeHPContainer.hxx"
25 #include "SalomeContainerTmpForHP.hxx"
26 #include "AutoGIL.hxx"
27
28 #include "PythonPorts.hxx"
29 #include "YacsTrace.hxx"
30 #include "PyStdout.hxx"
31
32 using namespace YACS::ENGINE;
33 using namespace std;
34
35 const char DistributedPythonNode::KIND[]="DistPython";
36 const char DistributedPythonNode::IMPL_NAME[]="Python";
37 const char DistributedPythonNode::SALOME_CONTAINER_METHOD_IDL[]="createPyNode";
38
39 Node *DistributedPythonNode::simpleClone(ComposedNode *father, bool editionOnly) const
40 {
41   return new DistributedPythonNode(*this,father);
42 }
43
44 DistributedPythonNode::DistributedPythonNode(const std::string& name):ServerNode(name),_context(0),_pyfuncSer(0),_pyfuncUnser(0)
45 {
46   initMySelf();
47 }
48
49 DistributedPythonNode::DistributedPythonNode(const DistributedPythonNode& other, ComposedNode *father):ServerNode(other,father),_context(0),_pyfuncSer(0),_pyfuncUnser(0)
50 {
51   initMySelf();
52 }
53
54 DistributedPythonNode::~DistributedPythonNode()
55 {
56   AutoGIL agil;
57   Py_DECREF(_context);
58 }
59
60 void DistributedPythonNode::load()
61 {
62   bool isContAlreadyStarted(false);
63   if(_container)
64     isContAlreadyStarted=_container->isAlreadyStarted(this);
65   ServerNode::load();
66   {
67     AutoGIL agil;
68     if( PyDict_SetItemString( _context, "__builtins__", getSALOMERuntime()->getBuiltins() ))
69       {
70         stringstream msg;
71         msg << "Impossible to set builtins" << __FILE__ << ":" << __LINE__;
72         _errorDetails=msg.str();
73         throw Exception(msg.str());
74       }
75     const char picklizeScript[]="import pickle\ndef pickleForDistPyth2009(*args,**kws):\n  return pickle.dumps((args,kws),-1)\n\ndef unPickleForDistPyth2009(st):\n  args=pickle.loads(st)\n  return args\n";
76     PyObject *res=PyRun_String(picklizeScript,Py_file_input,_context,_context);
77     if(res == NULL)
78       {
79         _errorDetails="";
80         PyObject* new_stderr = newPyStdOut(_errorDetails);
81         PySys_SetObject((char*)"stderr", new_stderr);
82         PyErr_Print();
83         PySys_SetObject((char*)"stderr", PySys_GetObject((char*)"__stderr__"));
84         Py_DECREF(new_stderr);
85         throw Exception("Error during execution");
86         return;
87       }
88     Py_DECREF(res);
89     _pyfuncSer=PyDict_GetItemString(_context,"pickleForDistPyth2009");
90     _pyfuncUnser=PyDict_GetItemString(_context,"unPickleForDistPyth2009");
91     if(_pyfuncSer == NULL)
92       {
93         _errorDetails="";
94         PyObject* new_stderr = newPyStdOut(_errorDetails);
95         PySys_SetObject((char*)"stderr", new_stderr);
96         PyErr_Print();
97         PySys_SetObject((char*)"stderr", PySys_GetObject((char*)"__stderr__"));
98         Py_DECREF(new_stderr);
99         throw Exception("Error during execution");
100       }
101     if(_pyfuncUnser == NULL)
102       {
103         _errorDetails="";
104         PyObject* new_stderr = newPyStdOut(_errorDetails);
105         PySys_SetObject((char*)"stderr", new_stderr);
106         PyErr_Print();
107         PySys_SetObject((char*)"stderr", PySys_GetObject((char*)"__stderr__"));
108         Py_DECREF(new_stderr);
109         throw Exception("Error during execution");
110       }
111
112     Engines::Container_var objContainer=Engines::Container::_nil();
113     if(!_container)
114       throw Exception("No container specified !");
115     SalomeContainer *containerCast0(dynamic_cast<SalomeContainer *>(_container));
116     SalomeHPContainer *containerCast1(dynamic_cast<SalomeHPContainer *>(_container));
117     if(containerCast0)
118       objContainer=containerCast0->getContainerPtr(this);
119     else if(containerCast1)
120       {
121         YACS::BASES::AutoCppPtr<SalomeContainerTmpForHP> tmpCont(SalomeContainerTmpForHP::BuildFrom(containerCast1,this));
122         objContainer=tmpCont->getContainerPtr(this);
123       }
124     else
125       throw Exception("Unrecognized type of container ! Salome one is expected !");
126     if(CORBA::is_nil(objContainer))
127       throw Exception("Container corba pointer is NULL !");
128
129     try
130     {
131         if(containerCast0 || !isContAlreadyStarted)
132           {
133             _pynode = objContainer->createPyNode(getName().c_str(),getScript().c_str());
134           }
135         else
136           {
137             Engines::PyNode_var dftPyScript(objContainer->getDefaultPyNode(getName().c_str()));
138             if(CORBA::is_nil(dftPyScript))
139               _pynode = objContainer->createPyNode(getName().c_str(),getScript().c_str());
140             else
141               _pynode = dftPyScript;
142           }
143     }
144     catch( const SALOME::SALOME_Exception& ex )
145     {
146         std::string msg="Exception on remote python node creation ";
147         msg += '\n';
148         msg += ex.details.text.in();
149         _errorDetails=msg;
150         throw Exception(msg);
151     }
152
153     if(CORBA::is_nil(_pynode))
154       throw Exception("In DistributedPythonNode the ref in NULL ! ");
155
156
157     DEBTRACE( "---------------End PyfuncSerNode::load function---------------" );
158   }
159 }
160
161 void DistributedPythonNode::execute()
162 {
163   YACSTRACE(1,"+++++++++++++++++ DistributedPythonNode::execute: " << getName() << " " << getFname() << " +++++++++++++++++" );
164   //////
165   PyObject* ob;
166   if(!_pyfuncSer)
167     throw Exception("DistributedPythonNode badly loaded");
168   Engines::pickledArgs *serializationInputCorba(0);
169   PyObject *args(0);
170   {
171     AutoGIL agil;
172
173     DEBTRACE( "---------------DistributedPythonNode::inputs---------------" );
174     args = PyTuple_New(getNumberOfInputPorts()) ;
175     int pos=0;
176     for(list<InputPort *>::iterator iter2 = _setOfInputPort.begin(); iter2 != _setOfInputPort.end(); iter2++,pos++)
177       {
178         InputPyPort *p=(InputPyPort *)*iter2;
179         ob=p->getPyObj();
180         Py_INCREF(ob);
181         PyTuple_SetItem(args,pos,ob);
182       }
183     PyObject *serializationInput=PyObject_CallObject(_pyfuncSer,args);
184     Py_ssize_t len = PyBytes_Size(serializationInput);
185     char* serializationInputC = PyBytes_AsString(serializationInput);
186     //int ret = PyBytes_AsStringAndSize(serializationInput, &serializationInputC, &len);
187     serializationInputCorba=new Engines::pickledArgs;
188     serializationInputCorba->length(len+1);
189     for(int i=0;i<len+1;i++)
190       (*serializationInputCorba)[i]=serializationInputC[i];
191     Py_DECREF(serializationInput);
192   }
193   //serializationInputCorba[serializationInputC.length()]='\0';
194   DEBTRACE( "-----------------DistributedPythonNode starting remote python invocation-----------------" );
195   Engines::pickledArgs *resultCorba;
196   try
197   {
198       resultCorba=_pynode->execute(getFname().c_str(),*serializationInputCorba);
199   }
200   catch(...)
201   {
202       std::string msg="Exception on remote python invocation";
203       _errorDetails=msg;
204       throw Exception(msg);
205   }
206   DEBTRACE( "-----------------DistributedPythonNode end of remote python invocation-----------------" );
207   //
208   delete serializationInputCorba;
209   char *resultCorbaC=new char[resultCorba->length()+1];
210   resultCorbaC[resultCorba->length()]='\0';
211   for(int i=0;i<resultCorba->length();i++)
212     resultCorbaC[i]=(*resultCorba)[i];
213   int lenResCorba=resultCorba->length();
214   delete resultCorba;
215   {
216     AutoGIL agil;
217     args = PyTuple_New(1);
218     //PyObject* resultPython=PyBytes_FromString(resultCorbaC);
219     PyObject* resultPython=PyBytes_FromStringAndSize(resultCorbaC,lenResCorba);
220     delete [] resultCorbaC;
221     PyTuple_SetItem(args,0,resultPython);
222     PyObject *finalResult=PyObject_CallObject(_pyfuncUnser,args);
223     DEBTRACE( "-----------------DistributedPythonNode::outputs-----------------" );
224     if(finalResult == NULL)
225       {
226         std::stringstream msg;
227         msg << "Conversion with pickle of output ports failed !";
228         msg << " : " << __FILE__ << ":" << __LINE__;
229         _errorDetails=msg.str();
230         throw YACS::ENGINE::ConversionException(msg.str());
231       }
232     int nres=1;
233     if(finalResult == Py_None)
234       nres=0;
235     else if(PyTuple_Check(finalResult))
236       nres=PyTuple_Size(finalResult);
237
238     if(getNumberOfOutputPorts() != nres)
239       {
240         std::string msg="Number of output arguments : Mismatch between definition and execution";
241         Py_DECREF(finalResult);
242         _errorDetails=msg;
243         throw Exception(msg);
244       }
245     try
246     {
247         int pos(0);
248         for(list<OutputPort *>::iterator iter = _setOfOutputPort.begin(); iter != _setOfOutputPort.end(); iter++, pos++)
249           {
250             OutputPyPort *p=(OutputPyPort *)*iter;
251             DEBTRACE( "port name: " << p->getName() );
252             DEBTRACE( "port kind: " << p->typeName() );
253             DEBTRACE( "port pos : " << pos );
254             if(PyTuple_Check(finalResult))ob=PyTuple_GetItem(finalResult,pos) ;
255             else ob=finalResult;
256             DEBTRACE( "ob refcnt: " << ob->ob_refcnt );
257             p->put(ob);
258           }
259     }
260     catch(ConversionException& ex)
261     {
262         Py_DECREF(finalResult);
263         _errorDetails=ex.what();
264         throw;
265     }
266   }
267   DEBTRACE( "++++++++++++++ End DistributedPythonNode::execute: " << getName() << " ++++++++++++++++++++" );
268 }
269
270 std::string DistributedPythonNode::getEffectiveKindOfServer() const
271 {
272   return "Salome";
273 }
274
275 std::string DistributedPythonNode::getKind() const
276 {
277   //! not a bug : this is to use classical python port translators.
278   return PythonNode::KIND;
279 }
280
281 ServerNode *DistributedPythonNode::createNode(const std::string& name) const
282 {
283   ServerNode *ret=new DistributedPythonNode(name);
284   ret->setContainer(_container);
285   return ret;
286 }
287
288 void DistributedPythonNode::initMySelf()
289 {
290   _implementation = DistributedPythonNode::IMPL_NAME;
291   AutoGIL agil;
292   _context=PyDict_New();
293 }
294
295 void DistributedPythonNode::dealException(CORBA::Exception *exc, const char *method, const char *ref)
296 {
297   if( exc )
298     {
299       DEBTRACE( "An exception was thrown!" );
300       DEBTRACE( "The raised exception is of Type:" << exc->_name() );
301
302       CORBA::SystemException* sysexc;
303       sysexc=CORBA::SystemException::_downcast(exc);
304       if(sysexc != NULL)
305         {
306           // It's a SystemException
307           DEBTRACE( "minor code: " << sysexc->minor() );
308           DEBTRACE( "completion code: " << sysexc->completed() );
309           std::string text="Execution problem: ";
310           std::string excname=sysexc->_name();
311           if(excname == "BAD_OPERATION")
312             {
313               text=text+"bad operation detected";
314             }
315           else if(excname == "MARSHAL" && sysexc->minor() == omni::MARSHAL_PassEndOfMessage)
316             {
317               text=text+"probably an error in arguments of service '" + method + "' from component '" +ref+ "'";
318             }
319           else if(excname == "COMM_FAILURE" && sysexc->minor() == omni::COMM_FAILURE_UnMarshalResults)
320             {
321               text=text+"probably an error in output arguments of service '" + method + "' from component '" +ref+ "'";
322             }
323           else if(excname == "COMM_FAILURE" && sysexc->minor() == omni::COMM_FAILURE_UnMarshalArguments)
324             {
325               text=text+"probably an error in input arguments of service '" + method + "' from component '" +ref+ "'";
326             }
327           else if(excname == "COMM_FAILURE" && sysexc->minor() == omni::COMM_FAILURE_WaitingForReply)
328             {
329               text=text+"probably an error in input arguments of service '" + method + "' from component '" +ref+ "'";
330             }
331           else
332             {
333               DEBTRACE(sysexc->NP_minorString() );
334               text=text+"System Exception "+ excname;
335             }
336           _errorDetails=text;
337           throw Exception(text);
338         }
339
340       // Not a System Exception
341       CORBA::UnknownUserException* userexc;
342       userexc=CORBA::UnknownUserException::_downcast(exc);
343       if(userexc != NULL)
344         {
345           CORBA::Any anyExcept = userexc->exception(); 
346
347           const SALOME::SALOME_Exception* salexc;
348           if(anyExcept >>= salexc)
349             {
350               DEBTRACE("SALOME_Exception: "<< salexc->details.sourceFile);
351               DEBTRACE("SALOME_Exception: "<<salexc->details.lineNumber);
352               _errorDetails=salexc->details.text;
353               throw Exception("Execution problem: Salome Exception occurred" + getErrorDetails() );
354             }
355           std::string msg="Execution problem: User Exception occurred";
356           _errorDetails=msg;
357           throw Exception(msg);
358         }
359       std::string msg="Execution problem";
360       _errorDetails=msg;
361       throw Exception(msg);
362     }
363 }