From: boulant Date: Wed, 26 Oct 2011 16:15:01 +0000 (+0000) Subject: ADD a end user module (services.py) to help the manipulation of SALOME KERNEL service... X-Git-Tag: Before_0020136~6 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=adc6bfb8b72649e5797b3d47fecda37f8ed4ae45;p=modules%2Fkernel.git ADD a end user module (services.py) to help the manipulation of SALOME KERNEL services from a python context (someone who is a master of salome.py and all the associated stuff does not need this module) --- diff --git a/src/KERNEL_PY/kernel/Makefile.am b/src/KERNEL_PY/kernel/Makefile.am index 73a02e2b4..91c7852c7 100644 --- a/src/KERNEL_PY/kernel/Makefile.am +++ b/src/KERNEL_PY/kernel/Makefile.am @@ -21,21 +21,27 @@ include $(top_srcdir)/salome_adm/unix/make_common_starter.am mypkgpythondir =$(salomepypkgdir)/kernel +# This first set of modules could be used in any context, you just +# have to get a python environement. mypkgpython_PYTHON = \ - __init__.py \ - deprecation.py \ - logger.py \ - studyedit.py \ - termcolor.py \ - logconfig.py \ - unittester.py \ + __init__.py \ + deprecation.py \ + logger.py \ + termcolor.py \ + logconfig.py \ + unittester.py \ pyunittester.py \ - enumerate.py \ - uiexception.py \ - datamodeler.py \ - diclookup.py \ - services.py \ - testdata.py + enumerate.py \ + uiexception.py \ + datamodeler.py \ + testdata.py \ + diclookup.py \ + threadhelper.py + +# This set of modules required a running SALOME application. +mypkgpython_PYTHON += \ + studyedit.py \ + services.py bin_SCRIPTS=\ kernelpy_autotest.sh diff --git a/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in b/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in index f2dd3337e..2608dbb95 100644 --- a/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in +++ b/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in @@ -52,9 +52,9 @@ listfiles="\ # logconfig.py" # This list REQUIRES a running SALOME application -#listfiles=$listfiles"\ -# services.py \ -# studyedit.py" +listfiles=$listfiles"\ + services.py \ + studyedit.py" # This files are not used yet (but we test them) #listfiles=$listfiles"\ diff --git a/src/KERNEL_PY/kernel/services.py b/src/KERNEL_PY/kernel/services.py index 304cd2f0b..d80046507 100644 --- a/src/KERNEL_PY/kernel/services.py +++ b/src/KERNEL_PY/kernel/services.py @@ -20,6 +20,17 @@ # Author: Guillaume Boulant (EDF/R&D) +# +# WARNING: development notes +# +# This python module is aimed to be a (yet another?) end user +# interface to manipulate the KERNEL SALOME services. Note that it +# does not replace the salome module (i.e. what you get when typing +# "import salome", actually implemented in the file __init__.py of the +# salome python package). It provides instead functions that help to +# use the salome module in a end user context (development of domain +# specific tools by programmers that are not ten years experienced in +# SALOME development). import salome if salome.lcc is None: @@ -27,7 +38,25 @@ if salome.lcc is None: salome.salome_init() except RuntimeError, e: print e - + +# Note that the salome module provides you with standard SALOME +# objects: CORBA broker (orb): salome.orb lyfe cycle (lcc) : +# salome.lcc naming service : salome.naming_service study manager : +# salome.myStudyManager The default study : salome.myStudy +# +# Alternatively, you may obtain these objects directly with the +# following instructions: +# +# from omniORB import CORBA +# from LifeCycleCORBA import LifeCycleCORBA +# orb = CORBA.ORB_init( [''], CORBA.ORB_ID ) +# lcc = LifeCycleCORBA( orb ) +# +# from SALOME_NamingServicePy import SALOME_NamingServicePy_i +# naming_service = SALOME_NamingServicePy_i( orb ) +# +# (See salome.py page in the KERNEL documentation) + # # ============================================================================== # Helper functions for SALOME components @@ -57,6 +86,9 @@ if salome.lcc is None: def getComponent(componentName = "SalomeTestComponent", corbaModule = "Engines", containerType = "FactoryServer"): + """ + Get a SALOME CORBA component from its name + """ print "INF: getting component %s from CORBA module %s ..."%(componentName,corbaModule) __import__(corbaModule) component=salome.lcc.FindOrLoadComponent(containerType,componentName) @@ -70,12 +102,30 @@ def getComponent(componentName = "SalomeTestComponent", # the list of components defined in the current session. import SALOME_ModuleCatalog def getComponentList(): + """ + Get the list of names of all SALOME componenents register in + the catalog. + """ obj = salome.naming_service.Resolve('Kernel/ModulCatalog') catalog = obj._narrow(SALOME_ModuleCatalog.ModuleCatalog) if not catalog: raise RuntimeError, "Can't accesss module catalog" return catalog.GetComponentList() +import SALOMEDS +def getStudyManager(): + """ + Get a study manager to create and manage SALOME studies. WARN: you + should use instead the variable salome.myStudyManager. This + function is given for illustration of usage of the naming service + """ + import SALOMEDS + naming_service = SALOME_NamingServicePy_i( orb ) + obj = naming_service.Resolve( '/myStudyManager' ) + studyManager = obj._narrow( SALOMEDS.StudyManager) + return studyManager + + # # ============================================================================== # Basic use cases and unit tests @@ -99,5 +149,5 @@ def TEST_getComponentList(): if __name__ == "__main__": import unittester - unittester.run("salome/kernel/__init__","TEST_getComponent") - unittester.run("salome/kernel/__init__","TEST_getComponentList") + unittester.run("services","TEST_getComponent") + unittester.run("services","TEST_getComponentList") diff --git a/src/KERNEL_PY/kernel/threadhelper.py b/src/KERNEL_PY/kernel/threadhelper.py new file mode 100644 index 000000000..ba9c6f503 --- /dev/null +++ b/src/KERNEL_PY/kernel/threadhelper.py @@ -0,0 +1,385 @@ +# -*- coding: iso-8859-1 -*- +# 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 +# + +__author__="gboulant" +__date__ ="$1 avr. 2010 18:12:38$" + +import time +import threading + +# =========================================================================== +class Runner(threading.Thread): + """ + This class provides a tool to run and drive a function in a dedicated thread. + """ + def __init__(self, functionToRun=None,*argv): + threading.Thread.__init__( self ) + self._stopevent = threading.Event() + self.setFunction(functionToRun) + self.setArguments(*argv) + self._exception = None + self._return = None + self._observer = None + self._callbackFunction = None + # We create an id from the name and the time date in milliseconds + self._id = functionToRun.__name__ +"/"+ str(time.time()) + + def setFunction(self,functionToRun): + """ + Positionne la fonction � ex�cuter. La fonction peut �tre la + m�thode d'un objet pass�e sous la forme 'monobjet.mamethode'. + """ + self.clear() + self._function = functionToRun + + def setArguments(self,*argv): + """ + Positionne les arguments � passer � la fonction + """ + self.clear() + self._argv = argv + + def getReturn(self): + """ + Retourne le resultat de la fonction. En cas d'erreur, on + r�cup�rera l'exception lev�e au moyen de la m�thode + getException(). + """ + return self._return + + def setCallbackFunction(self, callbackFunction): + self._callbackFunction = callbackFunction + + def setObserver(self, observerToNotify): + """ + Permet de sp�cifier un observateur � notifier en fin + d'ex�cution. L'observateur est suppos� �tre un objet qui + impl�mente une m�thode processNotification() + """ + try: + observerToNotify.processNotification + except AttributeError: + raise DevelException("The observer should implement the method processNotification()") + + self._observer = observerToNotify + + def run(self): + """ + Ex�cution de la fonction. Impl�mentation de la m�thode run + d�clench�e par l'appel � Thread.start(). + """ + print "##################### threadhelper.run" + if self._function is None: return + try: + self._return = self._function(*self._argv) + except Exception, e: + print e + self._exception = e + self._stopevent.set() + self.notifyObserver() + self.callback() + # _GBO_ Maybe it's no use to have both observers and a callback function. + # One of the two mechanism should be sufficient + + def notifyObserver(self): + if self._observer is None: + # Aucune notification pr�vue + return + try: + self._observer.processNotification() + except AttributeError, att: + if str(att) == "processNotification": + print "L'observateur n'impl�mente pas la m�thode processNotification()" + else: + print "La fonction processNotification() a lev� une exception:" + print att + except Exception, e: + print "La fonction processNotification() a lev� une exception:" + print e + + def callback(self): + if self._callbackFunction is None: return + self._callbackFunction() + + def isEnded(self): + """ + Retourne true si la fonction s'est termin�e naturellement + (correctement ou en erreur). Utile pour les client qui + pratique le pooling (interrogation r�p�t�e � intervalle + r�gulier. + """ + return self._stopevent.isSet() + # _GBO_ On n'utilise pas isAlive() pour pouvoir ma�triser + # l'indicateur de stop (exemple de la fonction kill) + # return not self.isAlive() + + def getException(self): + return self._exception + + def getId(self): + return self._id + + def wait(self,timeout=None): + """ + Met fin au thread apr�s timeout seconde s'il est encore en + ex�cution. + Si le compte-�-rebours est atteind sans que la fonction + functionToRun soit termin�e, alors le runner est laiss� dans + l'�tat 'not Ended', c'est-�-dire que isEnded retourne + false. On peut ainsi distinguer l'arr�t normal de l'arr�t � + l'issue du timeout. + """ + threading.Thread.join(self, timeout) + + + def kill(self,message="Arr�t demand�"): + """ + Cette m�thode doit �tre appeler pour interrombre le + thread. Cet appel d�clare le thread comme en erreur (exception + mise � disposition par la m�thode getException(). + """ + # On cr�e un exception indiquant la demande d'interruption + self._exception = Exception(message) + self._stopevent.set() + # _GBO_ ATTENTION + # Un thread python ne peut pas en r�alit� �tre int�rrompu. + # La fonction reste donc en ex�cution m�me si on a rejoint le + # thread appelant. + + def clear(self): + self._stopevent.clear() + self._exception = None + self._return = None + +# =========================================================================== +CONTINUE=1 +STOP=0 + +class PeriodicTimer( threading.Thread ): + """ + Cette classe permet d'amorcer un compte-�-rebours p�riodique pour + op�rer un m�canisme de pooling. On d�finit la fonction appell�e + p�riodiquement et la fonction terminale. + """ + def __init__( self, loopdelay, initdelay=0, + periodic_action=None, + ended_action=None, + *ended_argv ): + + if periodic_action is None: + self._periodic_action = self.defaultPeriodicAction + else: + self._periodic_action = periodic_action + self._ended_action = ended_action + self._loopdelay = loopdelay + self._initdelay = initdelay + self._running = CONTINUE + self._ended_argv = ended_argv + threading.Thread.__init__( self ) + + def defaultPeriodicAction(): + """ + Les fonctions 'periodicAction' retournent CONTINU ou STOP + apr�s avoir ex�cut� l'action de fin de boucle. Si STOP est + retourn�, le cycle est interrompu. + """ + return CONTINU + + def run( self ): + if self._initdelay: + time.sleep( self._initdelay ) + self._runtime = time.time() + while self._running == CONTINUE: + start = time.time() + self._running = self._periodic_action() + self._runtime += self._loopdelay + time.sleep( max( 0, self._runtime - start ) ) + if self._ended_action is not None: + self._ended_action( *self._ended_argv ) + + def stop( self ): + self._running = STOP + + + +# +# ====================================================== +# Fonctions de test unitaire +# ====================================================== +# + +# ====================================================== +import os +testfilename="/tmp/threadhelperTestFile" +def testIfContinue(): + print "On examine la pr�sence du fichier ", testfilename + if os.path.exists(testfilename): + return STOP + else: + return CONTINUE + +def endedAction(): + print "FINI" + +def TEST_PeriodicTimer(): + periodicTimer=PeriodicTimer(1,0,testIfContinue, endedAction) + periodicTimer.start() + + +# ====================================================== +def function_ok(nbsteps=5): + """ + Fonction qui se termine correctement + """ + print "D�but" + cnt=0 + while ( cnt < nbsteps ): + print "Etape ", cnt + time.sleep(0.6) + cnt+=1 + + print "Fin" + +def function_with_exception(): + """ + Fonction qui aboutie � une lev�e d'exception + """ + print "D�but" + cnt=0 + while ( cnt < 5 ): + print "Etape ", cnt + time.sleep(1) + cnt+=1 + + raise Exception("erreur d'ex�cution de la fonction") + print "Fin" + +def infinite_function(): + """ + fonction de dur�e infinie (tant qu'il y a du courant �l�ctrique) pour + le test du timeout. + """ + print "D�but" + cnt=0 + while ( 1 ): + print "Etape ", cnt + time.sleep(1) + cnt+=1 + + raise Exception("erreur") + print "Fin" + + +def runWithRunner(functionToRun): + """ + Ex�cute la fonction avec le runner. On illustre ici la modalit� + d'utilisation du Runner. + """ + print "###########" + runner = Runner(functionToRun) + runner.start() + + while ( not runner.isEnded() ): + print "La fonction est en cours" + time.sleep(0.2) + e = runner.getException() + if e is not None: + print "La fonction s'est termin�e en erreur" + print e + # On peut en fait la relancer + # raise e + else: + print "La fonction s'est termin�e correctement" + + +def runWithTimeout(functionToRun, timeout=10): + """ + Ex�cute la fonction avec le runner. On illustre ici la modalit� + d'utilisation du Runner. + """ + print "runWithTimeout : DEBUT" + runner = Runner(functionToRun) + runner.start() + + # On se fixe un temps au del� duquel on consid�re que la fonction + # est en erreur => on tue le thread (timeout) + runner.wait(timeout) + print "Apr�s runner.timeout(timeout)" + if not runner.isEnded(): + runner.kill() + e = runner.getException() + if e is not None: + print "La fonction s'est termin�e en erreur" + print e + # On peut en fait la relancer + # raise e + else: + print "La fonction s'est termin�e correctement" + + print "runWithTimeout : FIN" + import sys + sys.exit(0) + + +def TEST_Timeout(): + #runWithTimeout(function_ok) + #runWithTimeout(function_ok,2) + runWithTimeout(function_with_exception) + + +def TEST_Runner(): + runWithRunner(function_ok) + runWithRunner(function_with_exception) + #runWithRunner(infinite_function) + + +def myCallbackFunction(): + print "myCallbackFunction: the job is ended" + + +def TEST_runWithCallback(): + runner = Runner(function_ok,8) + runner.setCallbackFunction(myCallbackFunction) + runner.start() + + if runner.getException() is not None: + return False + + runnerId = runner.getId() + print "A runner has been started with id="+str(runnerId) + cpt = 0 + while ( not runner.isEnded() ): + print "Waiting notification from process "+str(runner.getId())+", step n°"+str(cpt) + time.sleep(0.2) + cpt+=1 + + return True + +if __name__ == "__main__": + import unittester + #TEST_PeriodicTimer() + #TEST_Runner() + #TEST_Timeout() + unittester.run("threadhelper","TEST_runWithCallback") + +