]> SALOME platform Git repositories - modules/kernel.git/blob - src/Container/SALOME_PyNode.py
Salome HOME
[EDF30057] : Management of SSD non shared fs for out of core management.
[modules/kernel.git] / src / Container / SALOME_PyNode.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 #  File   : SALOME_PyNode.py
22 #  Author : Christian CAREMOLI, EDF
23 #  Module : SALOME
24 #  $Header$
25 #
26 import sys,traceback
27 import linecache
28 import pickle
29 import Engines__POA
30 import SALOME__POA
31 import SALOME
32 import logging
33 import KernelBasis
34 import abc
35 import os
36 import sys
37 from SALOME_ContainerHelper import ScriptExecInfo
38
39 MY_CONTAINER_ENTRY_IN_GLBS = "my_container"
40
41 MY_PERFORMANCE_LOG_ENTRY_IN_GLBS = "my_log_4_this_session"
42
43 MY_KEY_TO_DETECT_FINISH = "neib av tuot"
44
45 class Generic(SALOME__POA.GenericObj):
46   """A Python implementation of the GenericObj CORBA IDL"""
47   def __init__(self,poa):
48     self.poa=poa
49     self.cnt=1
50
51   def Register(self):
52     #print("Register called : %d"%self.cnt)
53     self.cnt+=1
54
55   def UnRegister(self):
56     #print("UnRegister called : %d"%self.cnt)
57     self.cnt-=1
58     if self.cnt <= 0:
59       oid=self.poa.servant_to_id(self)
60       self.poa.deactivate_object(oid)
61
62   def Destroy(self):
63     print("WARNING SALOME::GenericObj::Destroy() function is obsolete! Use UnRegister() instead.")
64     self.UnRegister()
65
66   def __del__(self):
67     #print("Destuctor called")
68     pass
69
70 class PyNode_i (Engines__POA.PyNode,Generic):
71   """The implementation of the PyNode CORBA IDL"""
72   def __init__(self, nodeName,code,poa,my_container):
73     """Initialize the node : compilation in the local context"""
74     Generic.__init__(self,poa)
75     self.nodeName=nodeName
76     self.code=code
77     self.my_container=my_container._container
78     linecache.cache[nodeName]=0,None,code.split('\n'),nodeName
79     ccode=compile(code,nodeName,'exec')
80     self.context={}
81     self.context[MY_CONTAINER_ENTRY_IN_GLBS] = self.my_container
82     exec(ccode, self.context)
83
84   def getContainer(self):
85     return self.my_container
86
87   def getCode(self):
88     return self.code
89
90   def getName(self):
91     return self.nodeName
92
93   def defineNewCustomVar(self,varName,valueOfVar):
94     self.context[varName] = pickle.loads(valueOfVar)
95     pass
96
97   def executeAnotherPieceOfCode(self,code):
98     """Called for initialization of container lodging self."""
99     try:
100       ccode=compile(code,self.nodeName,'exec')
101       exec(ccode, self.context)
102     except Exception:
103       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"","PyScriptNode (%s) : code to be executed \"%s\"" %(self.nodeName,code),0))
104
105   def execute(self,funcName,argsin):
106     """Execute the function funcName found in local context with pickled args (argsin)"""
107     try:
108       argsin,kws=pickle.loads(argsin)
109       func=self.context[funcName]
110       argsout=func(*argsin,**kws)
111       argsout=pickle.dumps(argsout,-1)
112       return argsout
113     except Exception:
114       exc_typ,exc_val,exc_fr=sys.exc_info()
115       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
116       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyNode: %s, function: %s" % (self.nodeName,funcName),0))
117
118 class SenderByte_i(SALOME__POA.SenderByte,Generic):
119   def __init__(self,poa,bytesToSend):
120     Generic.__init__(self,poa)
121     self.bytesToSend = bytesToSend
122
123   def getSize(self):
124     return len(self.bytesToSend)
125
126   def sendPart(self,n1,n2):
127     return self.bytesToSend[n1:n2]
128   
129 def IsRemote(hostName):
130     import socket
131     return socket.gethostname() != hostName
132
133 def RemoveFileSafe( fileName ):
134     if os.path.exists( fileName ):
135       os.unlink( fileName )
136
137 def RetrieveRemoteFileLocallyInSameFileName( remoteHostName, fileName):
138     """ To customize"""
139     dn = os.path.dirname( fileName )
140     import subprocess as sp
141     p = sp.Popen(["scp","{}:{}".format(remoteHostName,fileName),dn])
142     p.communicate()
143
144 def DestroyRemotely( remoteHostName, fileName):
145     import subprocess as sp
146     p = sp.Popen(["ssh","-qC","-oStrictHostKeyChecking=no","-oBatchMode=yes",remoteHostName,"rm {}".format( fileName )])
147     p.communicate()
148
149 class CopyFileFromRemoteCtxMgr:
150   def __init__(self, hostName, fileName):
151     self._remoteHostName = hostName
152     self._fileName = fileName
153     self._isRemote = IsRemote( hostName )
154
155   def __enter__(self):
156     if not self._isRemote:
157       return
158     dn = os.path.dirname( self._fileName )
159     if not os.path.isdir( dn ):
160       os.mkdir( dn )
161     RetrieveRemoteFileLocallyInSameFileName(self._remoteHostName,self._fileName)
162     
163   def __exit__(self,exctype, exc, tb):
164     if not self._isRemote:
165       return
166     os.unlink( self._fileName )
167   
168 class BigFileOnDiskBase(abc.ABC):
169   """
170   Base class in charge of managing 
171   Copy or share of file accross computation Nodes
172   """
173   def __init__(self, fileName):
174     self._file_name = fileName
175
176   def getFileName(self):
177     return self._file_name
178
179   @abc.abstractmethod
180   def get(self, visitor = None):
181     """
182     Method called client side of data.
183     """
184     raise RuntimeError("Not implemented !")
185   
186   @abc.abstractmethod
187   def unlink(self):
188     """
189     Method called client side of data.
190     """
191     raise RuntimeError("Not implemented !")
192
193
194 class BigFileOnDiskShare(BigFileOnDiskBase):
195   def __init__(self, fileName):
196     super().__init__( fileName )
197
198   def get(self, visitor = None):
199     return GetObjectFromFile( self._file_name, visitor )
200   
201   def unlink(self):
202     RemoveFileSafe( self._file_name )
203
204 class BigFileOnDiskSSDNoShare(BigFileOnDiskBase):
205   def __init__(self, fileName):
206     import socket
207     super().__init__( fileName )
208     # hostname hosting data
209     self._hostname = socket.gethostname()
210
211   def get(self, visitor = None):
212     with CopyFileFromRemoteCtxMgr(self._hostname, self._file_name):
213       return GetObjectFromFile( self._file_name, visitor )
214     
215   def unlink(self):
216     if IsRemote( self._hostname ):
217       DestroyRemotely(self._hostname,self._file_name)
218     else:
219       RemoveFileSafe( self._file_name )
220
221 BigFileOnDiskClsFromProtocol = { 0 : BigFileOnDiskShare, 1 : BigFileOnDiskSSDNoShare }
222
223 DicoForProxyFile = { }
224
225 def GetSizeOfBufferedReader(f):
226   """
227   This method returns in bytes size of a file openned.
228
229   Args:
230   ----
231       f (io.IOBase): buffered reader returned by open
232       
233   Returns
234   -------
235       int: number of bytes
236   """
237   import io
238   pos = f.tell()
239   f.seek(0,io.SEEK_END)
240   pos2 = f.tell()
241   f.seek(pos,io.SEEK_SET)
242   return pos2-pos
243
244 def GetObjectFromFile(fname, visitor = None):
245   with open(fname,"rb") as f:
246     if visitor:
247       visitor.setHDDMem( GetSizeOfBufferedReader(f) )
248       visitor.setFileName( fname )
249     obj = pickle.load(f)
250   return obj
251
252 def DumpInFile(obj,fname):
253   with open(fname,"wb") as f:
254     f.write( obj )
255
256 def IncrRefInFile(fname):
257   """
258   :param fname:
259   :type fname: str
260   """
261   if fname in DicoForProxyFile:
262     DicoForProxyFile[fname] += 1
263   else:
264     DicoForProxyFile[fname] = 2
265   pass
266
267 def DecrRefInFile(fname):
268   """
269   :param fname:
270   :type fname: BigFileOnDiskBase
271   """
272   if fname not in DicoForProxyFile:
273     cnt = 1
274   else:
275     cnt = DicoForProxyFile[fname.getFileName()]
276     DicoForProxyFile[fname.getFileName()] -= 1
277     if cnt == 1:
278       del DicoForProxyFile[fname.getFileName()]
279   if cnt == 1:
280     fname.unlink()
281   pass
282
283 def GetBigObjectOnDiskThreshold():
284     return KernelBasis.GetBigObjOnDiskThreshold()
285
286 def ActivateProxyMecanismOrNot( sizeInByte ):
287   thres = GetBigObjectOnDiskThreshold()
288   if thres == -1:
289     return False
290   else:
291     return sizeInByte > thres
292
293 def GetBigObjectDirectory():
294   import os
295   protocol, directory = KernelBasis.GetBigObjOnDiskProtocolAndDirectory()
296   if not directory:
297     raise RuntimeError("An object of size higher than limit detected and no directory specified to dump it in file !")
298   return protocol, os.path.expanduser( os.path.expandvars( directory ) )
299
300 def GetBigObjectFileName():
301   """
302   Return a filename in the most secure manner (see tempfile documentation)
303   """
304   import tempfile
305   protocol, directory = GetBigObjectDirectory()
306   with tempfile.NamedTemporaryFile(dir = directory, prefix="mem_", suffix=".pckl") as f:
307     ret = f.name
308   return BigFileOnDiskClsFromProtocol[protocol]( ret )
309
310 class BigObjectOnDiskBase:
311   def __init__(self, fileName, objSerialized):
312     """
313     :param fileName: the file used to dump into.
314     :param objSerialized: the object in pickeled form
315     :type objSerialized: bytes
316     """
317     self._filename = fileName
318     # attribute _destroy is here to tell client side or server side
319     # only client side can be with _destroy set to True. server side due to risk of concurrency
320     # so pickled form of self must be done with this attribute set to False.
321     self._destroy = False
322     self.__dumpIntoFile(objSerialized)
323
324   def getDestroyStatus(self):
325     return self._destroy
326
327   def incrRef(self):
328     if self._destroy:
329       IncrRefInFile( self._filename )
330     else:
331       # should never happen !
332       RuntimeError("Invalid call to incrRef !")
333
334   def decrRef(self):
335     if self._destroy:
336       DecrRefInFile( self._filename )
337     else:
338       # should never happen !
339       RuntimeError("Invalid call to decrRef !")
340
341   def unlinkOnDestructor(self):
342     self._destroy = True
343
344   def doNotTouchFile(self):
345     """
346     Method called slave side. The life cycle management of file is client side not slave side.
347     """
348     self._destroy = False
349
350   def __del__(self):
351     if self._destroy:
352       DecrRefInFile( self._filename )
353
354   def getFileName(self):
355     return self._filename
356   
357   def __dumpIntoFile(self, objSerialized):
358     DumpInFile( objSerialized, self._filename.getFileName() )
359
360   def get(self, visitor = None):
361     return self._filename.get(visitor)
362
363   def __float__(self):
364     return float( self.get() )
365     
366   def __int__(self):
367     return int( self.get() )
368     
369   def __str__(self):
370     obj = self.get()
371     if isinstance(obj,str):
372         return obj
373     else:
374         raise RuntimeError("Not a string")
375       
376 class BigObjectOnDisk(BigObjectOnDiskBase):
377   def __init__(self, fileName, objSerialized):
378     BigObjectOnDiskBase.__init__(self, fileName, objSerialized)
379     
380 class BigObjectOnDiskListElement(BigObjectOnDiskBase):
381   def __init__(self, pos, length, fileName):
382     self._filename = fileName
383     self._destroy = False
384     self._pos = pos
385     self._length = length
386
387   def get(self, visitor = None):
388     fullObj = BigObjectOnDiskBase.get(self, visitor)
389     return fullObj[ self._pos ]
390     
391   def __getitem__(self, i):
392     return self.get()[i]
393
394   def __len__(self):
395     return len(self.get())
396     
397 class BigObjectOnDiskSequence(BigObjectOnDiskBase):
398   def __init__(self, length, fileName, objSerialized):
399     BigObjectOnDiskBase.__init__(self, fileName, objSerialized)
400     self._length = length
401
402   def __getitem__(self, i):
403     return BigObjectOnDiskListElement(i, self._length, self.getFileName())
404
405   def __len__(self):
406     return self._length
407
408 class BigObjectOnDiskList(BigObjectOnDiskSequence):
409   def __init__(self, length, fileName, objSerialized):
410     BigObjectOnDiskSequence.__init__(self, length, fileName, objSerialized)
411     
412 class BigObjectOnDiskTuple(BigObjectOnDiskSequence):
413   def __init__(self, length, fileName, objSerialized):
414     BigObjectOnDiskSequence.__init__(self, length, fileName, objSerialized)
415
416 def ProxyfyPickeled( obj, pickleObjInit = None, visitor = None ):
417   """
418   This method return a proxy instance of pickled form of object given in input.
419
420   Args:
421   ----
422       obj (pickelable type) : object to be proxified
423       pickleObjInit (bytes) : Optionnal. Original pickeled form of object to be proxyfied if already computed. If not this method generate it
424
425   Returns
426   -------
427       BigObjectOnDiskBase: proxy instance
428   """
429   pickleObj = pickleObjInit
430   if pickleObj is None:
431     pickleObj = pickle.dumps( obj , pickle.HIGHEST_PROTOCOL )
432   fileName = GetBigObjectFileName()
433   if visitor:
434     visitor.setHDDMem( len(pickleObj) )
435     visitor.setFileName( fileName.getFileName() )
436   if isinstance( obj, list):
437     proxyObj = BigObjectOnDiskList( len(obj), fileName, pickleObj )
438   elif isinstance( obj, tuple):
439     proxyObj = BigObjectOnDiskTuple( len(obj), fileName , pickleObj )
440   else:
441     proxyObj = BigObjectOnDisk( fileName , pickleObj )
442   return proxyObj
443
444 def SpoolPickleObject( obj, visitor = None ):
445   import pickle
446   with InOutputObjVisitorCM(visitor) as v:
447     pickleObjInit = pickle.dumps( obj , pickle.HIGHEST_PROTOCOL )
448     if not ActivateProxyMecanismOrNot( len(pickleObjInit) ):
449       return pickleObjInit
450     else:
451       proxyObj = ProxyfyPickeled( obj, pickleObjInit, v.visitor() )
452       pickleProxy = pickle.dumps( proxyObj , pickle.HIGHEST_PROTOCOL )
453       return pickleProxy
454
455 from SALOME_ContainerHelper import InOutputObjVisitorCM, InOutputObjVisitor
456
457 def UnProxyObjectSimple( obj, visitor = None ):
458   """
459   Method to be called in Remote mode. Alterate the obj _status attribute. 
460   Because the slave process does not participate in the reference counting
461   
462   Args:
463   ----
464       visitor (InOutputObjVisitor): A visitor to keep track of amount of memory on chip and those on HDD
465
466   """
467   with InOutputObjVisitorCM(visitor) as v:
468     logging.debug( "UnProxyObjectSimple {}".format(type(obj)) )
469     if isinstance(obj,BigObjectOnDiskBase):
470       obj.doNotTouchFile()
471       return obj.get( v )
472     elif isinstance( obj, list):
473       retObj = []
474       for elt in obj:
475         retObj.append( UnProxyObjectSimple(elt,v.visitor()) )
476       return retObj
477     else:
478       return obj
479
480 def UnProxyObjectSimpleLocal( obj ):
481   """
482   Method to be called in Local mode. Do not alterate the PyObj counter
483   """
484   if isinstance(obj,BigObjectOnDiskBase):
485     return obj.get()
486   elif isinstance( obj, list):
487     retObj = []
488     for elt in obj:
489       retObj.append( UnProxyObjectSimpleLocal(elt) )
490     return retObj
491   else:
492     return obj
493   
494 class FileHolder:
495   def __init__(self, fileName):
496     self._filename = fileName
497   @property
498   def filename(self):
499     return self._filename
500   
501 class FileDeleter(FileHolder):
502   def __init__(self, fileName):
503     super().__init__( fileName )
504   def __del__(self):
505     import os
506     if os.path.exists( self._filename ):
507       os.unlink( self._filename )
508
509 class MonitoringInfo:
510   def __init__(self, pyFileName, intervalInMs, outFileName, pid):
511     self._py_file_name = pyFileName
512     self._interval_in_ms = intervalInMs
513     self._out_file_name = outFileName
514     self._pid = pid
515
516   @property
517   def pyFileName(self):
518     return self._py_file_name
519
520   @property
521   def pid(self):
522     return self._pid
523   
524   @pid.setter
525   def pid(self, value):
526     self._pid = value
527
528   @property
529   def outFileName(self):
530     return self._out_file_name
531   
532   @property
533   def intervalInMs(self):
534     return self._interval_in_ms
535   
536 def FileSystemMonitoring(intervalInMs, dirNameToInspect, outFileName = None):
537     """
538     This method loops indefinitely every intervalInMs milliseconds to scan 
539     number of inodes and size of content recursively included into the in input directory.
540
541     Args:
542     ----
543
544     outFileName (str) : name of file inside the results will be written. If None a new file is generated
545
546     See also CPUMemoryMonitoring
547     """
548     global orb
549     import os
550     dirNameToInspect2 = os.path.abspath( os.path.expanduser(dirNameToInspect) )
551     import tempfile
552     import logging
553     import KernelBasis
554     # outFileNameSave stores the content of outFileName during phase of dumping
555     with tempfile.NamedTemporaryFile(prefix="fs_monitor_",suffix=".txt") as f:
556       outFileNameSave = f.name
557     with tempfile.NamedTemporaryFile(prefix="fs_monitor_",suffix=".py") as f:
558       tempPyFile = f.name
559     tempOutFile = outFileName
560     if tempOutFile is None:
561       tempOutFile = "{}.txt".format( os.path.splitext( tempPyFile )[0] )
562     with open(tempPyFile,"w") as f:
563         f.write("""
564 import subprocess as sp
565 import re
566 import os
567 import time
568 import datetime
569 with open("{tempOutFile}","a") as f:
570   f.write( "{{}}\\n".format( "{dirNameToInspect2}" ) )
571   f.write( "{{}}\\n".format( "{intervalInMs}" ) )
572   while(True):
573     nbinodes = -1
574     try:
575       nbinodes = sp.check_output("{{}} | wc -l".format( " ".join(["find","{dirNameToInspect2}"]),  ), shell = True).decode().strip()
576     except:
577       pass
578     szOfDirStr = "fail"
579     try:
580       st = sp.check_output(["du","-sh","{dirNameToInspect2}"]).decode()
581       szOfDirStr = re.split("[\s]+",st)[0]
582     except:
583       pass
584     f.write( "{{}}\\n".format( str( datetime.datetime.now().timestamp() ) ) )
585     f.write( "{{}}\\n".format( str( nbinodes  ) ) )
586     f.write( "{{}}\\n".format( str( szOfDirStr ) ) )
587     f.flush()
588     time.sleep( {intervalInMs} / 1000.0 )
589 """.format( **locals()))
590     logging.debug( "File for FS monitoring dump file : {}".format(tempPyFile) )
591     pyFileName = FileDeleter( tempPyFile )
592     if outFileName is None:
593       outFileName = FileDeleter( tempOutFile )
594     else:
595       outFileName = FileHolder(outFileName)
596     return MonitoringInfo(pyFileName, intervalInMs, outFileName, None)
597
598 def CPUMemoryMonitoring( intervalInMs, outFileName = None ):
599   """
600   Launch a subprocess monitoring self process.
601   This monitoring subprocess is a python process lauching every intervalInMs ms evaluation of
602   CPU usage and RSS memory of the calling process.
603   Communication between subprocess and self is done by file.
604
605   Args:
606   ----
607     outFileName (str) : name of file inside the results will be written. If None a new file is generated
608
609   See also FileSystemMonitoring
610   """
611   import KernelBasis
612   def BuildPythonFileForCPUPercent( intervalInMs, outFileName):
613     import os
614     import tempfile
615     with tempfile.NamedTemporaryFile(prefix="cpu_mem_monitor_",suffix=".py") as f:
616       tempPyFile = f.name
617     tempOutFile = outFileName
618     if tempOutFile is None:
619       tempOutFile = "{}.txt".format( os.path.splitext( tempPyFile )[0] )
620     pid = os.getpid()
621     with open(tempPyFile,"w") as f:
622       f.write("""import psutil
623 pid = {}
624 process = psutil.Process( pid )
625 def getChargeOf( p ):
626   a,b = p.cpu_percent(), p.memory_info().rss
627   try:
628     for c in p.children():
629       a += c.cpu_percent(interval=0.01) ; b += c.memory_info().rss
630   except:
631     pass
632   return a,b
633 import time
634 with open("{}","a") as f:
635   f.write( "{{}}\\n".format( "{}" ) )
636   while True:
637     cpu,mem_rss = getChargeOf( process )
638     f.write( "{{}}\\n".format( str( cpu ) ) )
639     f.write( "{{}}\\n".format( str( mem_rss  ) ) )
640     f.flush()
641     time.sleep( {} / 1000.0 )
642 """.format(pid, tempOutFile, intervalInMs, intervalInMs))
643     if outFileName is None:
644       autoOutFile = FileDeleter(tempOutFile)
645     else:
646       autoOutFile = FileHolder(tempOutFile)
647     return FileDeleter(tempPyFile),autoOutFile
648   pyFileName, outFileName = BuildPythonFileForCPUPercent( intervalInMs, outFileName )
649   return MonitoringInfo(pyFileName, intervalInMs, outFileName, None)
650
651 class GenericPythonMonitoringLauncherCtxMgr:
652     def __init__(self, monitoringParams):
653         """
654         Args:
655         ----
656             monitoringParams (MonitoringInfo)
657         """
658         self._monitoring_params = monitoringParams
659     def __enter__(self):
660         import KernelBasis
661         pid = KernelBasis.LaunchMonitoring(self._monitoring_params.pyFileName.filename)
662         self._monitoring_params.pid = pid
663         return self._monitoring_params
664     
665     def __exit__(self,exctype, exc, tb):
666         StopMonitoring( self._monitoring_params )
667         del self._monitoring_params
668         import gc
669         gc.collect() # force destruction of objects even in raise context
670
671 def StopMonitoring( monitoringInfo ):
672   """
673   Kill monitoring subprocess.
674
675   Args:
676   ----
677       monitoringInfo (MonitoringInfo): info returned by LaunchMonitoring
678   """
679   import KernelBasis
680   KernelBasis.StopMonitoring(monitoringInfo.pid)
681
682 class CPUMemInfo:
683   def __init__(self, intervalInMs, cpu, mem_rss):
684     """
685     Args:
686     ----
687     intervalInMs (int)
688     cpu (list<float>)  CPU usage
689     mem_rss (list<int>) rss memory usage
690     """
691     self._interval_in_ms = intervalInMs
692     self._data = [(a,b) for a,b in zip(cpu,mem_rss)]
693   def __str__(self):
694     st = """Interval in ms : {self.intervalInMs}
695 Data : ${self.data}
696 """.format( **locals() )
697     return st
698   @property
699   def intervalInMs(self):
700     return self._interval_in_ms
701   @property
702   def data(self):
703     """
704     list of triplets. First param of pair is cpu usage 
705                       Second param of pair is memory usage
706     """
707     return self._data
708
709 def ReadCPUMemInfoInternal( fileName ):
710   intervalInMs = 0
711   cpu = [] ; mem_rss = []
712   if os.path.exists( fileName ):
713     try:
714       with open(fileName, "r") as f:
715         coarseData = [ elt.strip() for elt in f.readlines() ]
716       intervalInMs = int( coarseData[0] )
717       coarseData = coarseData[1:]
718       cpu = [float(elt) for elt in coarseData[::2]]
719       mem_rss = [ int(elt) for elt in coarseData[1::2]]
720     except:
721       pass
722   return CPUMemInfo(intervalInMs,cpu,mem_rss)
723
724 def ReadCPUMemInfo( monitoringInfo ):
725   """
726   Retrieve CPU/Mem data of monitoring.
727
728   Args:
729   ----
730       monitoringInfo (MonitoringInfo): info returned by LaunchMonitoring
731   
732   Returns
733   -------
734     CPUMemInfo instance
735   """
736   return ReadCPUMemInfoInternal( monitoringInfo.outFileName.filename )
737
738 class InodeSizeInfo:
739   def __init__(self, dirNameMonitored, intervalInMs, timeStamps, nbInodes, volumeOfDir):
740     """
741     Args:
742     ----
743     timeStamps (list<datetimestruct>)
744     nbInodes (list<int>)
745     volumeOfDir (list<str>)
746     """
747     self._dir_name_monitored = dirNameMonitored
748     self._interval_in_ms = intervalInMs
749     self._data = [(t,a,b) for t,a,b in zip(timeStamps,nbInodes,volumeOfDir)]
750   def __str__(self):
751     st = """Filename monitored : {self.dirNameMonitored}
752 Interval in ms : ${self.intervalInMs}
753 Data : ${self.data}
754 """.format( **locals() )
755     return st
756   @property
757   def dirNameMonitored(self):
758     return self._dir_name_monitored
759   @property
760   def intervalInMs(self):
761     return self._interval_in_ms
762   @property
763   def data(self):
764     """
765     list of triplets. First param of triplet is datetimestruct
766                                       Second param of triplet is #inodes.
767                                       Thirst param of triplet is size.
768     """
769     return self._data
770
771 def ReadInodeSizeInfoInternal( fileName ):
772   import datetime
773   import os
774   with open(fileName, "r") as f:
775     coarseData = [ elt.strip() for elt in f.readlines() ]
776   dirNameMonitored = coarseData[0] ; intervalInMs = int( coarseData[1] ) ; coarseData = coarseData[2:]
777   tss = [ datetime.datetime.fromtimestamp( float(elt) ) for elt in coarseData[::3] ]
778   nbInodes = [int(elt) for elt in coarseData[1::3]]
779   volumeOfDir = coarseData[2::3]
780   return InodeSizeInfo(dirNameMonitored,intervalInMs,tss,nbInodes,volumeOfDir)
781
782 def ReadInodeSizeInfo( monitoringInfo ):
783   """
784   Retrieve nb of inodes and size of monitoring
785
786   Args:
787   ----
788       monitoringInfo (MonitoringInfo): info returned by LaunchMonitoring
789
790   Returns
791   -------
792     InodeSizeInfo
793   """
794   return ReadInodeSizeInfoInternal( monitoringInfo.outFileName.filename )
795
796 class SeqByteReceiver:
797   # 2GB limit to trigger split into chunks
798   CHUNK_SIZE = 2000000000
799   def __init__(self,sender):
800     self._obj = sender
801   def __del__(self):
802     self._obj.UnRegister()
803     pass
804   def data(self):
805     size = self._obj.getSize()
806     if size <= SeqByteReceiver.CHUNK_SIZE:
807       return self.fetchOneShot( size )
808     else:
809       return self.fetchByChunks( size )
810   def fetchOneShot(self,size):
811     return self._obj.sendPart(0,size)
812   def fetchByChunks(self,size):
813       """
814       To avoid memory peak parts over 2GB are sent using EFF_CHUNK_SIZE size.
815       """
816       data_for_split_case = bytes(0)
817       EFF_CHUNK_SIZE = SeqByteReceiver.CHUNK_SIZE // 8
818       iStart = 0 ; iEnd = EFF_CHUNK_SIZE
819       while iStart!=iEnd and iEnd <= size:
820         part = self._obj.sendPart(iStart,iEnd)
821         data_for_split_case = bytes(0).join( [data_for_split_case,part] )
822         iStart = iEnd; iEnd = min(iStart + EFF_CHUNK_SIZE,size)
823       return data_for_split_case
824   
825 FinalCode = """import pickle
826 from SALOME_PyNode import LogOfCurrentExecutionSession,MY_PERFORMANCE_LOG_ENTRY_IN_GLBS
827 import CORBA
828 import Engines
829 orb = CORBA.ORB_init([''])
830 codeFileName = "{}"
831 inputFileName = "{}"
832 outputFileName = "{}"
833 outputsKeys = {}
834 exec( "{{}} = LogOfCurrentExecutionSession( orb.string_to_object( \\"{}\\" ) )".format(MY_PERFORMANCE_LOG_ENTRY_IN_GLBS) )
835 with open(inputFileName,"rb") as f:
836   context = pickle.load( f )
837 context[MY_PERFORMANCE_LOG_ENTRY_IN_GLBS] = eval( MY_PERFORMANCE_LOG_ENTRY_IN_GLBS )
838 with open(codeFileName,"r") as f:
839   code = f.read()
840 # go for execution
841 exec( code , context )
842 # filter part of context to be exported to father process
843 context = dict( [(k,v) for k,v in context.items() if k in outputsKeys] )
844 #
845 with open(outputFileName,"wb") as f:
846   pickle.dump( context, f )
847 """
848
849 class PythonFunctionEvaluatorParams:
850   def __init__(self, mainFileName, codeFileName, inContextFileName, outContextFileName):
851     self._main_filename = mainFileName
852     self._code_filename = codeFileName
853     self._in_context_filename = inContextFileName
854     self._out_context_filename = outContextFileName
855   @property
856   def result(self):
857     import pickle
858     with open(self._out_context_filename,"rb") as f:
859       return pickle.load( f )
860   def destroyOnOK(self):
861     for fileToDestroy in [self._main_filename,self._code_filename,self._in_context_filename,self._out_context_filename]:
862       if os.path.exists( fileToDestroy ):
863         os.unlink( fileToDestroy )
864   def destroyOnKO(self, containerRef):
865      """
866      Called in the context of failure with replay mode activated
867      """
868      for fileToDestroy in [self._out_context_filename]:
869       if os.path.exists( fileToDestroy ):
870         os.unlink( fileToDestroy )
871       # register to container files group associated to the
872       containerRef.addLogFileNameGroup([self._main_filename,self._code_filename,self._in_context_filename])
873   @property
874   def replayCmd(self):
875     return "To replay : ( cd {} && python3 {} )".format(os.path.dirname(self._main_filename),os.path.basename(self._main_filename))
876   
877   @property
878   def cleanOperations(self):
879     import os
880     return "To clean files : ( cd {} && rm {} )".format( os.path.dirname(self._main_filename)," ".join( [os.path.basename(self._main_filename),self._code_filename,self._in_context_filename] ) )
881
882   def strDependingOnReturnCode(self, keepFilesToReplay, returnCode):
883     if returnCode == -1:
884       return f"return with non zero code ({returnCode})"
885     else:
886       banner = 200*"*"
887       if keepFilesToReplay:
888         return f"""return with non zero code ({returnCode})
889 {banner}
890 Looks like a hard crash as returnCode {returnCode} != 0
891 {self.replayCmd}
892 {self.cleanOperations}
893 {banner}
894 """
895       else:
896         return f"""return with non zero code ({returnCode})
897 {banner}
898 Looks like a hard crash as returnCode {returnCode} != 0
899 {banner}
900 """
901
902 def ExecCrashProofGeneric( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, keepFilesToReplay, closeEyesOnErrorAtExit):
903   """
904   Equivalent of exec(code,context) but executed in a separate subprocess to avoid to make the current process crash.
905   
906   Args:
907   -----
908
909   code (str) : python code to be executed using context
910   context (dict) : context to be used for execution. This context will be updated in accordance with the execution of code.
911   outargsname (list<str>) : list of arguments to be exported 
912   containerRef (Engines.Container) : Container ref (retrieving the Files to created when keepFilesToReplay is set to False)
913   instanceOfLogOfCurrentSession (LogOfCurrentExecutionSession) : instance of LogOfCurrentExecutionSession to build remotely the reference in order to log information
914   keepFilesToReplay (bool) : if True when something goes wrong during execution all the files to replay post mortem case are kept. If False only error is reported but files to replay are destoyed.
915   closeEyesOnErrorAtExit (bool) : if True in case of crash of subprocess, if MY_KEY_TO_DETECT_FINISH is displayed at the end of stdout
916
917   Return:
918   -------
919
920   ScriptExecInfo : instance serverside
921
922   In/Out:
923   -------
924
925   context will be modified by this method. elts in outargsname will be added and their corresponding value coming from evaluation.
926   """
927   import tempfile
928   import pickle
929   import subprocess as sp
930   import CORBA
931   #
932   def IsConsideredAsOKRun( returnCode, closeEyesOnErrorAtExit , stderr ):
933     def StdErrTreatment(closeEyesOnErrorAtExit , stderr):
934       if not closeEyesOnErrorAtExit:
935         return stderr
936       else:
937         return stderr[:-len(MY_KEY_TO_DETECT_FINISH)]
938     if returnCode == 0:
939       return True,StdErrTreatment(closeEyesOnErrorAtExit , stderr)
940     if not closeEyesOnErrorAtExit:
941       return False, stderr
942     if stderr[-len(MY_KEY_TO_DETECT_FINISH):] == MY_KEY_TO_DETECT_FINISH:
943       return True,stderr[:-len(MY_KEY_TO_DETECT_FINISH)]
944     else:
945       return False,stderr
946
947   #
948   def InternalExecResistant( code, context, outargsname):
949     import KernelBasis
950     orb = CORBA.ORB_init([''])
951     iorScriptLog = orb.object_to_string( instanceOfLogOfCurrentSession._remote_handle )#ref ContainerScriptPerfLog_ptr
952     ####
953     EXEC_CODE_FNAME_PXF = "execsafe_"
954     def RetrieveUniquePartFromPfx( fname ):
955       return os.path.splitext( os.path.basename(fname)[len(EXEC_CODE_FNAME_PXF):] )[0]
956     with tempfile.NamedTemporaryFile(dir=os.getcwd(),prefix=EXEC_CODE_FNAME_PXF,suffix=".py", mode="w", delete = False) as codeFd:
957       codeFd.write( "{}\n".format( containerRef.get_startup_code() ) )
958       codeFd.write( code )
959       if closeEyesOnErrorAtExit:
960         codeFd.write( """
961 import sys
962 sys.stderr.write({!r})
963 sys.stderr.flush()""".format( MY_KEY_TO_DETECT_FINISH ) )
964       codeFd.flush()
965       codeFileName = os.path.basename( codeFd.name )
966       contextFileName = "contextsafe_{}.pckl".format( RetrieveUniquePartFromPfx( codeFileName  ) )
967       with open(contextFileName,"wb") as contextFd:
968         pickle.dump( context, contextFd)
969       resFileName = "outcontextsafe_{}.pckl".format( RetrieveUniquePartFromPfx( codeFileName  ) )
970       mainExecFileName = os.path.abspath( "mainexecsafe_{}.py".format( RetrieveUniquePartFromPfx( codeFileName  ) ) )
971       with open(mainExecFileName,"w") as f:
972         f.write( FinalCode.format( codeFileName, contextFileName, resFileName, outargsname, iorScriptLog ) )
973       for iTry in range( KernelBasis.GetNumberOfRetry() ):
974         if iTry > 0:
975           print( "WARNING : Retry # {}. Following code has generated non zero return code ( {} ). Trying again ... \n{}".format( iTry, returnCode, code ) )
976         p = sp.Popen(["python3", mainExecFileName],stdout = sp.PIPE, stderr = sp.PIPE)
977         stdout, stderr = p.communicate()
978         returnCode = p.returncode
979         if returnCode == 0:
980           break
981     return returnCode, stdout, stderr, PythonFunctionEvaluatorParams(mainExecFileName,codeFileName,contextFileName,resFileName)
982   ret = instanceOfLogOfCurrentSession._current_instance
983   returnCode, stdout, stderr, evParams = InternalExecResistant( code, context, outargsname )
984   stdout = stdout.decode()
985   stderr = stderr.decode()
986   sys.stdout.write( stdout ) ; sys.stdout.flush()
987   isOK, stderr = IsConsideredAsOKRun( returnCode, closeEyesOnErrorAtExit , stderr )
988   sys.stderr.write( stderr ) ; sys.stderr.flush()
989   if isOK:
990     pcklData = instanceOfLogOfCurrentSession._remote_handle.getObj()
991     if len(pcklData) > 0:
992       ret = pickle.loads( pcklData )
993     context.update( evParams.result )
994     evParams.destroyOnOK()
995     if returnCode != 0:
996       print( "WARNING : Following code has generated non zero return code ( {} ) but considered as OK\n{}".format( returnCode, code ) )
997     return ret
998   else:
999     if keepFilesToReplay:
1000       evParams.destroyOnKO( containerRef )
1001     else:
1002       evParams.destroyOnOK()
1003     raise RuntimeError(f"Subprocess launched {evParams.strDependingOnReturnCode(keepFilesToReplay,returnCode)}stdout :\n{stdout}\nstderr :\n{stderr}")
1004
1005 def ExecCrashProofWithReplay( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1006   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, True, False)
1007
1008 def ExecCrashProofWithoutReplay( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1009   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, False, False)
1010
1011 def ExecCrashProofWithReplayFT( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1012   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, True, True)
1013
1014 def ExecCrashProofWithoutReplayFT( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1015   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, False, True)
1016
1017 def ExecLocal( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1018   exec( code, context )
1019   return instanceOfLogOfCurrentSession._current_instance
1020
1021 class LogOfCurrentExecutionSessionAbs(abc.ABC):
1022   def __init__(self):
1023     self._current_instance = ScriptExecInfo()
1024
1025   def addInfoOnLevel2(self, key, value):
1026     setattr(self._current_instance,key,value)
1027
1028   @abc.abstractmethod
1029   def addFreestyleAndFlush(self, value):
1030     raise RuntimeError("Must be overloaded")
1031
1032 class LogOfCurrentExecutionSession(LogOfCurrentExecutionSessionAbs):
1033   def __init__(self, handleToCentralizedInst):
1034     super().__init__()
1035     self._remote_handle = handleToCentralizedInst
1036
1037   def addFreestyleAndFlush(self, value):
1038     self._current_instance.freestyle = value
1039     self.finalizeAndPushToMaster()
1040
1041   def finalizeAndPushToMaster(self):
1042     """
1043     Voluntary do nothing in case of problem to avoid to trouble execution
1044     """
1045     try:
1046       self._remote_handle.assign( pickle.dumps( self._current_instance ) )
1047     except:
1048       pass
1049
1050 class LogOfCurrentExecutionSessionStub(LogOfCurrentExecutionSessionAbs):
1051   """
1052   This class is to stub LogOfCurrentExecutionSession in context of replay where the server (handleToCentralizedInst) has vanished
1053   """
1054   def __init__(self, handleToCentralizedInst = None):
1055     super().__init__()
1056   def addFreestyleAndFlush(self, value):
1057     pass
1058
1059 class PyScriptNode_Abstract_i(Engines__POA.PyScriptNode,Generic,abc.ABC):
1060   """The implementation of the PyScriptNode CORBA IDL that executes a script"""
1061   def __init__(self, nodeName, code, poa, my_container, logscript):
1062     """Initialize the node : compilation in the local context"""
1063     Generic.__init__(self,poa)
1064     self.nodeName=nodeName
1065     self.code=code
1066     self.my_container_py = my_container
1067     self.my_container=my_container._container
1068     linecache.cache[nodeName]=0,None,code.split('\n'),nodeName
1069     self.ccode=compile(code,nodeName,'exec')
1070     self.context={}
1071     self.context[MY_CONTAINER_ENTRY_IN_GLBS] = self.my_container
1072     self._log_script = logscript
1073     self._current_execution_session = None
1074     sys.stdout.flush() ; sys.stderr.flush() # flush to correctly capture log per execution session
1075
1076   @abc.abstractmethod
1077   def executeNow(self, outargsname):
1078     raise RuntimeError("Must be overloaded")
1079       
1080   def __del__(self):
1081     # force removal of self.context. Don t know why it s not done by default
1082     self.removeAllVarsInContext()
1083     pass
1084
1085   def getContainer(self):
1086     return self.my_container
1087
1088   def getCode(self):
1089     return self.code
1090
1091   def getName(self):
1092     return self.nodeName
1093
1094   def defineNewCustomVar(self,varName,valueOfVar):
1095     self.context[varName] = pickle.loads(valueOfVar)
1096     pass
1097
1098   def executeAnotherPieceOfCode(self,code):
1099     """Called for initialization of container lodging self."""
1100     try:
1101       ccode=compile(code,self.nodeName,'exec')
1102       exec(ccode, self.context)
1103     except Exception:
1104       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"","PyScriptNode (%s) : code to be executed \"%s\"" %(self.nodeName,code),0))
1105
1106   def assignNewCompiledCode(self,codeStr):
1107     try:
1108       self.code=codeStr
1109       self.ccode=compile(codeStr,self.nodeName,'exec')
1110     except Exception:
1111       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"","PyScriptNode.assignNewCompiledCode (%s) : code to be executed \"%s\"" %(self.nodeName,codeStr),0))
1112
1113   def executeSimple(self, key, val):
1114     """
1115     Same as execute method except that no pickelization mecanism is implied here. No output is expected
1116     """
1117     try:
1118       self.context.update({ "env" : [(k,v) for k,v in zip(key,val)]})
1119       exec(self.ccode,self.context)
1120     except Exception:
1121       exc_typ,exc_val,exc_fr=sys.exc_info()
1122       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1123       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1124       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" % (self.nodeName),0))
1125     
1126   def execute(self,outargsname,argsin):
1127     """Execute the script stored in attribute ccode with pickled args (argsin)"""
1128     try:
1129       argsname,kws=pickle.loads(argsin)
1130       self.context.update(kws)
1131       exec(self.ccode, self.context)
1132       argsout=[]
1133       for arg in outargsname:
1134         if arg not in self.context:
1135           raise KeyError("There is no variable %s in context" % arg)
1136         argsout.append(self.context[arg])
1137       argsout=pickle.dumps(tuple(argsout),-1)
1138       return argsout
1139     except Exception:
1140       exc_typ,exc_val,exc_fr=sys.exc_info()
1141       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1142       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1143       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s, outargsname: %s" % (self.nodeName,outargsname),0))
1144
1145   def executeFirst(self,argsin):
1146     """ Same than first part of self.execute to reduce memory peak."""
1147     def ArgInMananger(self,argsin):
1148       argsInPy = SeqByteReceiver( argsin )
1149       data = argsInPy.data()
1150       self.addInfoOnLevel2("inputMem",len(data))
1151       _,kws=pickle.loads(data)
1152       return kws
1153     try:
1154       self.beginOfCurrentExecutionSession()
1155       self.addTimeInfoOnLevel2("startInputTime")
1156       # to force call of SeqByteReceiver's destructor
1157       kws = ArgInMananger(self,argsin)
1158       vis = InOutputObjVisitor()
1159       for elt in kws:
1160         # fetch real data if necessary
1161         kws[elt] = UnProxyObjectSimple( kws[elt],vis)
1162       self.addInfoOnLevel2("inputHDDMem",vis)
1163       self.context.update(kws)
1164       self.addTimeInfoOnLevel2("endInputTime")
1165     except Exception:
1166       exc_typ,exc_val,exc_fr=sys.exc_info()
1167       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1168       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1169       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode:First %s" % (self.nodeName),0))
1170
1171   def executeSecond(self,outargsname):
1172     """ Same than second part of self.execute to reduce memory peak."""
1173     def executeSecondInternal(monitoringtimeresms):
1174       with GenericPythonMonitoringLauncherCtxMgr( CPUMemoryMonitoring( monitoringtimeresms ) ) as monitoringParams:
1175         currentInstance = self.executeNow( outargsname )
1176         cpumeminfo = ReadCPUMemInfo( monitoringParams )
1177       return cpumeminfo, currentInstance
1178
1179     import sys
1180     try:
1181       self.addTimeInfoOnLevel2("startExecTime")
1182       ##
1183       self.addInfoOnLevel2("measureTimeResolution",self.my_container_py.monitoringtimeresms())
1184       cpumeminfo, self._current_execution_session._current_instance = executeSecondInternal( self.my_container_py.monitoringtimeresms() )
1185       ##
1186       self.addInfoOnLevel2("CPUMemDuringExec",cpumeminfo)
1187       self.addTimeInfoOnLevel2("endExecTime")
1188       self.addTimeInfoOnLevel2("startOutputTime")
1189       argsout=[]
1190       for arg in outargsname:
1191         if arg not in self.context:
1192           raise KeyError("There is no variable %s in context" % arg)
1193         argsout.append(self.context[arg])
1194       ret = [ ]
1195       outputMem = 0
1196       vis = InOutputObjVisitor()
1197       for arg in argsout:
1198         # the proxy mecanism is catched here
1199         argPickle = SpoolPickleObject( arg, vis )
1200         retArg = SenderByte_i( self.poa,argPickle )
1201         id_o = self.poa.activate_object(retArg)
1202         retObj = self.poa.id_to_reference(id_o)
1203         ret.append( retObj._narrow( SALOME.SenderByte ) )
1204         outputMem += len(argPickle)
1205       self.addInfoOnLevel2("outputMem",outputMem)
1206       self.addInfoOnLevel2("outputHDDMem",vis)
1207       self.addTimeInfoOnLevel2("endOutputTime")
1208       self.endOfCurrentExecutionSession()
1209       return ret
1210     except Exception:
1211       exc_typ,exc_val,exc_fr=sys.exc_info()
1212       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1213       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1214       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode:Second %s, outargsname: %s" % (self.nodeName,outargsname),0))
1215
1216   def listAllVarsInContext(self):
1217       import re
1218       pat = re.compile("^__([a-z]+)__$")
1219       return [elt for elt in self.context if not pat.match(elt) and elt != MY_CONTAINER_ENTRY_IN_GLBS]
1220       
1221   def removeAllVarsInContext(self):
1222       for elt in self.listAllVarsInContext():
1223         del self.context[elt]
1224
1225   def getValueOfVarInContext(self,varName):
1226     try:
1227       return pickle.dumps(self.context[varName],-1)
1228     except Exception:
1229       exc_typ,exc_val,exc_fr=sys.exc_info()
1230       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1231       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1232     pass
1233   
1234   def assignVarInContext(self, varName, value):
1235     try:
1236       self.context[varName][0] = pickle.loads(value)
1237     except Exception:
1238       exc_typ,exc_val,exc_fr=sys.exc_info()
1239       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1240       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1241     pass
1242
1243   def callMethodOnVarInContext(self, varName, methodName, args):
1244     try:
1245       return pickle.dumps( getattr(self.context[varName][0],methodName)(*pickle.loads(args)),-1 )
1246     except Exception:
1247       exc_typ,exc_val,exc_fr=sys.exc_info()
1248       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1249       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1250     pass
1251
1252   def beginOfCurrentExecutionSession(self):
1253     self._current_execution_session = LogOfCurrentExecutionSession( self._log_script.addExecutionSession() )
1254     self.context[MY_PERFORMANCE_LOG_ENTRY_IN_GLBS] = self._current_execution_session
1255   
1256   def endOfCurrentExecutionSession(self):
1257     self._current_execution_session.finalizeAndPushToMaster()
1258     self._current_execution_session = None
1259
1260   def addInfoOnLevel2(self, key, value):
1261     self._current_execution_session.addInfoOnLevel2(key, value)
1262       
1263   def addTimeInfoOnLevel2(self, key):
1264     from datetime import datetime
1265     self._current_execution_session.addInfoOnLevel2(key,datetime.now())
1266
1267 class PyScriptNode_i(PyScriptNode_Abstract_i):
1268   def __init__(self, nodeName, code, poa, my_container, logscript):
1269     super().__init__(nodeName, code, poa, my_container, logscript)
1270
1271   def executeNow(self, outargsname):
1272     return ExecLocal(self.ccode,self.context,outargsname,self.my_container,self._current_execution_session)
1273     
1274 class PyScriptNode_OutOfProcess_i(PyScriptNode_Abstract_i):
1275   def __init__(self, nodeName, code, poa, my_container, logscript):
1276     super().__init__(nodeName, code, poa, my_container, logscript)
1277
1278   def executeNow(self, outargsname):
1279     return ExecCrashProofWithoutReplay(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1280
1281 class PyScriptNode_OutOfProcess_Replay_i(PyScriptNode_Abstract_i):
1282   def __init__(self, nodeName, code, poa, my_container, logscript):
1283     super().__init__(nodeName, code, poa, my_container, logscript)
1284
1285   def executeNow(self, outargsname):
1286     return ExecCrashProofWithReplay(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1287
1288 class PyScriptNode_OutOfProcess_FT_i(PyScriptNode_Abstract_i):
1289   def __init__(self, nodeName, code, poa, my_container, logscript):
1290     super().__init__(nodeName, code, poa, my_container, logscript)
1291
1292   def executeNow(self, outargsname):
1293     return ExecCrashProofWithoutReplayFT(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1294
1295 class PyScriptNode_OutOfProcess_Replay_FT_i(PyScriptNode_Abstract_i):
1296   def __init__(self, nodeName, code, poa, my_container, logscript):
1297     super().__init__(nodeName, code, poa, my_container, logscript)
1298
1299   def executeNow(self, outargsname):
1300     return ExecCrashProofWithReplayFT(self.code,self.context,outargsname,self.my_container,self._current_execution_session)