Version: 9.15.0
DistributedPythonNode.cxx
Go to the documentation of this file.
1 // Copyright (C) 2006-2025 CEA, EDF
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 
21 #include "RuntimeSALOME.hxx"
22 #include "SalomeContainer.hxx"
23 #include "PythonNode.hxx"
24 #include "SalomeHPContainer.hxx"
26 #include "PythonCppUtils.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 
55 {
56  AutoGIL agil;
57  Py_DECREF(_context);
58 }
59 
61 {
62  bool isContAlreadyStarted(false);
63  if(_container)
64  isContAlreadyStarted=_container->isAlreadyStarted(this);
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  {
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  DEBUG_YACSTRACE( "---------------End PyfuncSerNode::load function---------------" );
158  }
159 }
160 
162 {
163  GURU_YACSTRACE("+++++++++++++++++ DistributedPythonNode::execute: " << getName() << " " << getFname() << " +++++++++++++++++" );
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  GURU_YACSTRACE( "---------------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  DEBUG_YACSTRACE( "-----------------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  DEBUG_YACSTRACE( "-----------------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  GURU_YACSTRACE( "-----------------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  GURU_YACSTRACE( "port name: " << p->getName() );
252  GURU_YACSTRACE( "port kind: " << p->typeName() );
253  GURU_YACSTRACE( "port pos : " << pos );
254  if(PyTuple_Check(finalResult))ob=PyTuple_GetItem(finalResult,pos) ;
255  else ob=finalResult;
256  GURU_YACSTRACE( "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  GURU_YACSTRACE( "++++++++++++++ End DistributedPythonNode::execute: " << getName() << " ++++++++++++++++++++" );
268 }
269 
271 {
272  return "Salome";
273 }
274 
276 {
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 
289 {
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  GURU_YACSTRACE( "An exception was thrown!" );
300  GURU_YACSTRACE( "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  GURU_YACSTRACE( "minor code: " << sysexc->minor() );
308  GURU_YACSTRACE( "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  GURU_YACSTRACE(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  GURU_YACSTRACE("SALOME_Exception: "<< salexc->details.sourceFile);
351  GURU_YACSTRACE("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 }
int Py_ssize_t
Definition: PythonNode.cxx:45
#define DEBUG_YACSTRACE(msg)
Definition: YacsTrace.hxx:53
#define GURU_YACSTRACE(msg)
Definition: YacsTrace.hxx:55
Base class for all composed nodes.
std::string getName() const
virtual bool isAlreadyStarted(const Task *askingNode) const =0
Node * simpleClone(ComposedNode *father, bool editionOnly) const
DistributedPythonNode(const std::string &name)
ServerNode * createNode(const std::string &name) const
std::string getKind() const
this method returns the type of input/output expected
void dealException(CORBA::Exception *exc, const char *method, const char *ref)
std::string getEffectiveKindOfServer() const
this method returns the type of container expected
std::list< InputPort * > _setOfInputPort
virtual std::string getErrorDetails()
Give a description of error when node status is ERROR.
std::list< OutputPort * > _setOfOutputPort
virtual std::string getFname()
Definition: InlineNode.hxx:101
virtual void setContainer(Container *container)
Definition: InlineNode.cxx:105
virtual std::string getScript()
Definition: InlineNode.hxx:51
Class for Python Ports.
Definition: PythonPorts.hxx:74
Base class for all nodes.
Definition: Node.hxx:70
std::string _implementation
Definition: Node.hxx:97
const std::string & getName() const
Definition: Node.hxx:125
std::string _errorDetails
Definition: Node.hxx:93
virtual std::string typeName()
Definition: Proc.hxx:84
static const char KIND[]
Definition: PythonNode.hxx:122
static SalomeContainerTmpForHP * BuildFrom(const SalomeHPContainer *cont, const Task *askingNode)
Engines::Container_ptr getContainerPtr(const Task *askingNode) const
Proc * p
Definition: driver.cxx:216
YACSRUNTIMESALOME_EXPORT RuntimeSALOME * getSALOMERuntime()
PyObject * newPyStdOut(std::string &out)
Definition: PyStdout.cxx:129
def ref(target, callback=None)
Definition: CONNECTOR.py:120