From 0b2eac6aae6d431aa46b13658d078d3825281e4c Mon Sep 17 00:00:00 2001 From: caremoli Date: Thu, 8 Oct 2009 14:29:01 +0000 Subject: [PATCH] CCAR: add the PyNode object that can be created in a container to execute remote Python code. Example from python interpreter: import salome_pynode code="""def f(x): return 2*x """ node=container.createPyNode("mynode",code) y=node.execute("f",10) --- idl/Makefile.am | 4 +- idl/SALOME_Component.idl | 8 +++ idl/SALOME_PyNode.idl | 57 +++++++++++++++++++++ src/Container/Container_i.cxx | 46 +++++++++++++++++ src/Container/Makefile.am | 1 + src/Container/SALOME_Container.py | 13 +++++ src/Container/SALOME_Container_i.hxx | 2 + src/Container/SALOME_PyNode.py | 74 ++++++++++++++++++++++++++++ src/KERNEL_PY/Makefile.am | 1 + src/KERNEL_PY/salome_pynode.py | 61 +++++++++++++++++++++++ 10 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 idl/SALOME_PyNode.idl create mode 100644 src/Container/SALOME_PyNode.py create mode 100644 src/KERNEL_PY/salome_pynode.py diff --git a/idl/Makefile.am b/idl/Makefile.am index 556ebe1b1..744d555d5 100644 --- a/idl/Makefile.am +++ b/idl/Makefile.am @@ -46,6 +46,7 @@ BASEIDL_FILES = \ nstest.idl \ DSC_Engines.idl \ SALOME_Ports.idl \ + SALOME_PyNode.idl \ Palm_Ports.idl \ SALOME_PACOExtension.idl \ SALOME_ParamPorts.idl @@ -95,6 +96,7 @@ BASEIDL_SOURCES =\ nstestSK.cc \ DSC_EnginesSK.cc \ SALOME_PortsSK.cc \ + SALOME_PyNodeSK.cc \ Calcium_PortsSK.cc \ Palm_PortsSK.cc \ SALOME_PACOExtensionSK.cc \ @@ -106,7 +108,7 @@ DYNIDL_SRCS = \ SALOMEDSDynSK.cc SALOME_SessionDynSK.cc SALOME_RessourcesCatalogDynSK.cc \ DSC_EnginesDynSK.cc SALOME_ComponentDynSK.cc SALOME_GenericObjDynSK.cc \ Palm_PortsDynSK.cc SALOME_ExceptionDynSK.cc SALOMEDS_AttributesDynSK.cc \ - LoggerDynSK.cc SALOME_PACOExtensionDynSK.cc SALOME_ParamPortsDynSK.cc + LoggerDynSK.cc SALOME_PACOExtensionDynSK.cc SALOME_ParamPortsDynSK.cc SALOME_PyNodeDynSK.cc MPIIDL_SOURCES = \ SALOME_MPIObjectSK.cc \ diff --git a/idl/SALOME_Component.idl b/idl/SALOME_Component.idl index 6e70b41e1..fc555dc34 100644 --- a/idl/SALOME_Component.idl +++ b/idl/SALOME_Component.idl @@ -28,6 +28,7 @@ #include "SALOMEDS.idl" #include "SALOME_Exception.idl" +#include "SALOME_PyNode.idl" /*! \file SALOME_Component.idl \brief interfaces for Component and Container */ @@ -192,6 +193,13 @@ module Engines \param localFile the local file to create by copy */ void copyFile(in Container contai, in string remoteFile, in string localFile); + + //! Create a PyNode in the container + /*! + \param nodeName the name of the PyNode + \param code python code as text to load in the node + */ + PyNode createPyNode(in string nodeName, in string code) raises(SALOME::SALOME_Exception); }; /*! \brief Interface of the %component. diff --git a/idl/SALOME_PyNode.idl b/idl/SALOME_PyNode.idl new file mode 100644 index 000000000..cde0a0ad8 --- /dev/null +++ b/idl/SALOME_PyNode.idl @@ -0,0 +1,57 @@ +// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : SALOME_PyNode.idl +// Author : Christian CAREMOLI, EDF +// $Header: +// +#ifndef _SALOME_PYNODE_IDL_ +#define _SALOME_PYNODE_IDL_ + +#include "SALOME_GenericObj.idl" +#include "SALOME_Exception.idl" + +/*! \file SALOME_PyNode.idl \brief interface for remote python execution +*/ + +/*! \brief +This is a package of interfaces used for executing Python code on remote container +*/ +module Engines +{ + typedef sequence pickledArgs; + + interface PyNode : SALOME::GenericObj + { + + /*! \brief execute a python function defined in the node + + \param functionName the python function defined in the node to execute + \param inargs input argument values provided as a python pickle + \return output argument values as a python pickle + */ + pickledArgs execute(in string functionName,in pickledArgs inargs) raises (SALOME::SALOME_Exception); + + } ; + +}; + +#endif diff --git a/src/Container/Container_i.cxx b/src/Container/Container_i.cxx index e7636281b..f7124ac82 100644 --- a/src/Container/Container_i.cxx +++ b/src/Container/Container_i.cxx @@ -1411,3 +1411,49 @@ void Engines_Container_i::copyFile(Engines::Container_ptr container, const char* } } +/*! \brief create a PyNode object to execute remote python code + * \param nodeName the name of the node + * \param code the python code to load + * \return the PyNode + */ +Engines::PyNode_ptr Engines_Container_i::createPyNode(const char* nodeName, const char* code) +{ + Engines::PyNode_var node= Engines::PyNode::_nil(); + + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject *res = PyObject_CallMethod(_pyCont, + (char*)"create_pynode", + (char*)"ss", + nodeName, + code); + if(res==NULL) + { + //internal error + PyErr_Print(); + PyGILState_Release(gstate); + SALOME::ExceptionStruct es; + es.type = SALOME::INTERNAL_ERROR; + es.text = "can not create a python node"; + throw SALOME::SALOME_Exception(es); + } + long ierr=PyInt_AsLong(PyTuple_GetItem(res,0)); + PyObject* result=PyTuple_GetItem(res,1); + std::string astr=PyString_AsString(result); + Py_DECREF(res); + PyGILState_Release(gstate); + + if(ierr==0) + { + CORBA::Object_var obj = _orb->string_to_object(astr.c_str()); + node = Engines::PyNode::_narrow(obj); + return node._retn(); + } + else + { + SALOME::ExceptionStruct es; + es.type = SALOME::INTERNAL_ERROR; + es.text = astr.c_str(); + throw SALOME::SALOME_Exception(es); + } + +} diff --git a/src/Container/Makefile.am b/src/Container/Makefile.am index 331023d77..c3a870cbe 100644 --- a/src/Container/Makefile.am +++ b/src/Container/Makefile.am @@ -46,6 +46,7 @@ salomeinclude_HEADERS = \ # Scripts to be installed dist_salomescript_PYTHON =\ SALOME_ComponentPy.py \ + SALOME_PyNode.py \ SALOME_Container.py # These files are executable scripts diff --git a/src/Container/SALOME_Container.py b/src/Container/SALOME_Container.py index 73ba33123..cd806273b 100644 --- a/src/Container/SALOME_Container.py +++ b/src/Container/SALOME_Container.py @@ -34,11 +34,13 @@ import os import sys import string +import traceback from omniORB import CORBA, PortableServer import SALOMEDS import Engines, Engines__POA from SALOME_NamingServicePy import * from SALOME_ComponentPy import * +import SALOME_PyNode from SALOME_utilities import * from Utils_Identity import getShortHostName @@ -107,3 +109,14 @@ class SALOME_Container_i: return comp_iors + def create_pynode(self,nodeName,code): + try: + node=SALOME_PyNode.PyNode_i(nodeName,code,self._poa) + id_o = self._poa.activate_object(node) + comp_o = self._poa.id_to_reference(id_o) + comp_iors = self._orb.object_to_string(comp_o) + return 0,comp_iors + except: + exc_typ,exc_val,exc_fr=sys.exc_info() + l=traceback.format_exception(exc_typ,exc_val,exc_fr) + return 1,"".join(l) diff --git a/src/Container/SALOME_Container_i.hxx b/src/Container/SALOME_Container_i.hxx index e45b939e6..91d40f96f 100644 --- a/src/Container/SALOME_Container_i.hxx +++ b/src/Container/SALOME_Container_i.hxx @@ -32,6 +32,7 @@ #include #include CORBA_SERVER_HEADER(SALOME_Component) +#include CORBA_SERVER_HEADER(SALOME_PyNode) #include #include @@ -97,6 +98,7 @@ public: virtual Engines::Salome_file_ptr createSalome_file(const char* origFileName); void copyFile(Engines::Container_ptr container, const char* remoteFile, const char* localFile); + Engines::PyNode_ptr createPyNode(const char* nodeName, const char* code); // --- local C++ methods Engines::Component_ptr diff --git a/src/Container/SALOME_PyNode.py b/src/Container/SALOME_PyNode.py new file mode 100644 index 000000000..5b21f7725 --- /dev/null +++ b/src/Container/SALOME_PyNode.py @@ -0,0 +1,74 @@ +# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# File : SALOME_PyNode.py +# Author : Christian CAREMOLI, EDF +# Module : SALOME +# $Header$ +# + +import sys,traceback,string +import linecache +import cPickle +import Engines__POA +import SALOME__POA +import SALOME + +class Generic(SALOME__POA.GenericObj): + """A Python implementation of the GenericObj CORBA IDL""" + def __init__(self,poa): + self.poa=poa + self.cnt=1 + + def Register(self): + self.cnt+=1 + + def Destroy(self): + self.cnt-=1 + if self.cnt <= 0: + oid=self.poa.servant_to_id(self) + self.poa.deactivate_object(oid) + +class PyNode_i (Engines__POA.PyNode,Generic): + """The implementation of the PyNode CORBA IDL""" + def __init__(self, nodeName,code,poa): + """Initialize the node : compilation in the local context""" + Generic.__init__(self,poa) + self.nodeName=nodeName + self.code=code + linecache.cache[nodeName]=0,None,string.split(code,'\n'),nodeName + ccode=compile(code,nodeName,'exec') + self.context={} + exec ccode in self.context + + def execute(self,funcName,argsin): + """Execute the function funcName found in local context with pickled args (argsin)""" + try: + argsin,kws=cPickle.loads(argsin) + func=self.context[funcName] + argsout=func(*argsin,**kws) + argsout=cPickle.dumps(argsout,-1) + return argsout + except: + exc_typ,exc_val,exc_fr=sys.exc_info() + l=traceback.format_exception(exc_typ,exc_val,exc_fr) + raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyNode: %s, function: %s" % (self.nodeName,funcName),0)) + diff --git a/src/KERNEL_PY/Makefile.am b/src/KERNEL_PY/Makefile.am index c94ecdd7d..98200fc09 100755 --- a/src/KERNEL_PY/Makefile.am +++ b/src/KERNEL_PY/Makefile.am @@ -45,6 +45,7 @@ if CORBA_GEN iparameters.py \ salome_version.py \ salome_notebook.py \ + salome_pynode.py \ salome_genericobj.py endif diff --git a/src/KERNEL_PY/salome_pynode.py b/src/KERNEL_PY/salome_pynode.py new file mode 100644 index 000000000..0a8182b9f --- /dev/null +++ b/src/KERNEL_PY/salome_pynode.py @@ -0,0 +1,61 @@ +# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# File : salome_pynode.py +# Author : Christian CAREMOLI, EDF +# Module : SALOME +# $Header$ +# + +""" + When imported this module adds to CORBA proxy (from PyNode type) automatic pickle and unpickle + of arguments and results when calling execute method. It also converts the SALOME exception into a standard python + exception +""" +import omniORB +import cPickle +import SALOME +import Engines + +class SmartPyNode(Engines._objref_PyNode): + def __init__(self): + Engines._objref_PyNode.__init__(self) + + def execute(self,functionName,*args,**kws): + try: + args=cPickle.dumps((args,kws),-1) + results=Engines._objref_PyNode.execute(self,functionName,args) + x=cPickle.loads(results) + return x + except SALOME.SALOME_Exception, e: + raise ValueError(e.details.text) + + def __getattr__(self,name): + """ a trick to be able to call directly a remote method by its name : no need to use execute""" + if name[0]== '_': + raise AttributeError, name + def afunc(*args,**kws): + return self.execute(name,*args,**kws) + return afunc + +#Register the new proxy for PyNode +omniORB.registerObjref(Engines._objref_PyNode._NP_RepositoryId, SmartPyNode) + -- 2.39.2