Salome HOME
[EDF30399] : Steer directory hosting replay files
[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     dirForReplayFiles = KernelBasis.GetDirectoryForReplayFiles()
957     if not dirForReplayFiles:
958       raise RuntimeError("You are in context of exec resistant you have to position Directory hosting these files properly")
959     with tempfile.NamedTemporaryFile(dir=dirForReplayFiles,prefix=EXEC_CODE_FNAME_PXF,suffix=".py", mode="w", delete = False) as codeFd:
960       codeFd.write( "{}\n".format( containerRef.get_startup_code() ) )
961       codeFd.write( code )
962       if closeEyesOnErrorAtExit:
963         codeFd.write( """
964 import sys
965 sys.stderr.write({!r})
966 sys.stderr.flush()""".format( MY_KEY_TO_DETECT_FINISH ) )
967       codeFd.flush()
968       codeFileName = os.path.basename( codeFd.name )
969       contextFileName = os.path.join( dirForReplayFiles, "contextsafe_{}.pckl".format( RetrieveUniquePartFromPfx( codeFileName  ) ) )
970       with open(contextFileName,"wb") as contextFd:
971         pickle.dump( context, contextFd)
972       resFileName = os.path.join( dirForReplayFiles, "outcontextsafe_{}.pckl".format( RetrieveUniquePartFromPfx( codeFileName  ) ) )
973       mainExecFileName = os.path.join( dirForReplayFiles, "mainexecsafe_{}.py".format( RetrieveUniquePartFromPfx( codeFileName  ) ) )
974       with open(mainExecFileName,"w") as f:
975         f.write( FinalCode.format( codeFileName, contextFileName, resFileName, outargsname, iorScriptLog ) )
976       for iTry in range( KernelBasis.GetNumberOfRetry() ):
977         if iTry > 0:
978           print( "WARNING : Retry # {}. Following code has generated non zero return code ( {} ). Trying again ... \n{}".format( iTry, returnCode, code ) )
979         p = sp.Popen(["python3", mainExecFileName],cwd = dirForReplayFiles,stdout = sp.PIPE, stderr = sp.PIPE)
980         stdout, stderr = p.communicate()
981         returnCode = p.returncode
982         if returnCode == 0:
983           break
984     return returnCode, stdout, stderr, PythonFunctionEvaluatorParams(mainExecFileName,codeFileName,contextFileName,resFileName)
985   ret = instanceOfLogOfCurrentSession._current_instance
986   returnCode, stdout, stderr, evParams = InternalExecResistant( code, context, outargsname )
987   stdout = stdout.decode()
988   stderr = stderr.decode()
989   sys.stdout.write( stdout ) ; sys.stdout.flush()
990   isOK, stderr = IsConsideredAsOKRun( returnCode, closeEyesOnErrorAtExit , stderr )
991   sys.stderr.write( stderr ) ; sys.stderr.flush()
992   if isOK:
993     pcklData = instanceOfLogOfCurrentSession._remote_handle.getObj()
994     if len(pcklData) > 0:
995       ret = pickle.loads( pcklData )
996     context.update( evParams.result )
997     evParams.destroyOnOK()
998     if returnCode != 0:
999       print( "WARNING : Following code has generated non zero return code ( {} ) but considered as OK\n{}".format( returnCode, code ) )
1000     return ret
1001   else:
1002     if keepFilesToReplay:
1003       evParams.destroyOnKO( containerRef )
1004     else:
1005       evParams.destroyOnOK()
1006     raise RuntimeError(f"Subprocess launched {evParams.strDependingOnReturnCode(keepFilesToReplay,returnCode)}stdout :\n{stdout}\nstderr :\n{stderr}")
1007
1008 def ExecCrashProofWithReplay( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1009   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, True, False)
1010
1011 def ExecCrashProofWithoutReplay( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1012   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, False, False)
1013
1014 def ExecCrashProofWithReplayFT( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1015   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, True, True)
1016
1017 def ExecCrashProofWithoutReplayFT( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1018   return ExecCrashProofGeneric(code, context, outargsname, containerRef, instanceOfLogOfCurrentSession, False, True)
1019
1020 def ExecLocal( code, context, outargsname, containerRef, instanceOfLogOfCurrentSession ):
1021   exec( code, context )
1022   return instanceOfLogOfCurrentSession._current_instance
1023
1024 class LogOfCurrentExecutionSessionAbs(abc.ABC):
1025   def __init__(self):
1026     self._current_instance = ScriptExecInfo()
1027
1028   def addInfoOnLevel2(self, key, value):
1029     setattr(self._current_instance,key,value)
1030
1031   @abc.abstractmethod
1032   def addFreestyleAndFlush(self, value):
1033     raise RuntimeError("Must be overloaded")
1034
1035 class LogOfCurrentExecutionSession(LogOfCurrentExecutionSessionAbs):
1036   def __init__(self, handleToCentralizedInst):
1037     super().__init__()
1038     self._remote_handle = handleToCentralizedInst
1039
1040   def addFreestyleAndFlush(self, value):
1041     self._current_instance.freestyle = value
1042     self.finalizeAndPushToMaster()
1043
1044   def finalizeAndPushToMaster(self):
1045     """
1046     Voluntary do nothing in case of problem to avoid to trouble execution
1047     """
1048     try:
1049       self._remote_handle.assign( pickle.dumps( self._current_instance ) )
1050     except:
1051       pass
1052
1053 class LogOfCurrentExecutionSessionStub(LogOfCurrentExecutionSessionAbs):
1054   """
1055   This class is to stub LogOfCurrentExecutionSession in context of replay where the server (handleToCentralizedInst) has vanished
1056   """
1057   def __init__(self, handleToCentralizedInst = None):
1058     super().__init__()
1059   def addFreestyleAndFlush(self, value):
1060     pass
1061
1062 class PyScriptNode_Abstract_i(Engines__POA.PyScriptNode,Generic,abc.ABC):
1063   """The implementation of the PyScriptNode CORBA IDL that executes a script"""
1064   def __init__(self, nodeName, code, poa, my_container, logscript):
1065     """Initialize the node : compilation in the local context"""
1066     Generic.__init__(self,poa)
1067     self.nodeName=nodeName
1068     self.code=code
1069     self.my_container_py = my_container
1070     self.my_container=my_container._container
1071     linecache.cache[nodeName]=0,None,code.split('\n'),nodeName
1072     self.ccode=compile(code,nodeName,'exec')
1073     self.context={}
1074     self.context[MY_CONTAINER_ENTRY_IN_GLBS] = self.my_container
1075     self._log_script = logscript
1076     self._current_execution_session = None
1077     sys.stdout.flush() ; sys.stderr.flush() # flush to correctly capture log per execution session
1078
1079   @abc.abstractmethod
1080   def executeNow(self, outargsname):
1081     raise RuntimeError("Must be overloaded")
1082       
1083   def __del__(self):
1084     # force removal of self.context. Don t know why it s not done by default
1085     self.removeAllVarsInContext()
1086     pass
1087
1088   def getContainer(self):
1089     return self.my_container
1090
1091   def getCode(self):
1092     return self.code
1093
1094   def getName(self):
1095     return self.nodeName
1096
1097   def defineNewCustomVar(self,varName,valueOfVar):
1098     self.context[varName] = pickle.loads(valueOfVar)
1099     pass
1100
1101   def executeAnotherPieceOfCode(self,code):
1102     """Called for initialization of container lodging self."""
1103     try:
1104       ccode=compile(code,self.nodeName,'exec')
1105       exec(ccode, self.context)
1106     except Exception:
1107       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"","PyScriptNode (%s) : code to be executed \"%s\"" %(self.nodeName,code),0))
1108
1109   def assignNewCompiledCode(self,codeStr):
1110     try:
1111       self.code=codeStr
1112       self.ccode=compile(codeStr,self.nodeName,'exec')
1113     except Exception:
1114       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"","PyScriptNode.assignNewCompiledCode (%s) : code to be executed \"%s\"" %(self.nodeName,codeStr),0))
1115
1116   def executeSimple(self, key, val):
1117     """
1118     Same as execute method except that no pickelization mecanism is implied here. No output is expected
1119     """
1120     try:
1121       self.context.update({ "env" : [(k,v) for k,v in zip(key,val)]})
1122       exec(self.ccode,self.context)
1123     except Exception:
1124       exc_typ,exc_val,exc_fr=sys.exc_info()
1125       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1126       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1127       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" % (self.nodeName),0))
1128     
1129   def execute(self,outargsname,argsin):
1130     """Execute the script stored in attribute ccode with pickled args (argsin)"""
1131     try:
1132       argsname,kws=pickle.loads(argsin)
1133       self.context.update(kws)
1134       exec(self.ccode, self.context)
1135       argsout=[]
1136       for arg in outargsname:
1137         if arg not in self.context:
1138           raise KeyError("There is no variable %s in context" % arg)
1139         argsout.append(self.context[arg])
1140       argsout=pickle.dumps(tuple(argsout),-1)
1141       return argsout
1142     except Exception:
1143       exc_typ,exc_val,exc_fr=sys.exc_info()
1144       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1145       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1146       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s, outargsname: %s" % (self.nodeName,outargsname),0))
1147
1148   def executeFirst(self,argsin):
1149     """ Same than first part of self.execute to reduce memory peak."""
1150     def ArgInMananger(self,argsin):
1151       argsInPy = SeqByteReceiver( argsin )
1152       data = argsInPy.data()
1153       self.addInfoOnLevel2("inputMem",len(data))
1154       _,kws=pickle.loads(data)
1155       return kws
1156     try:
1157       self.beginOfCurrentExecutionSession()
1158       self.addTimeInfoOnLevel2("startInputTime")
1159       # to force call of SeqByteReceiver's destructor
1160       kws = ArgInMananger(self,argsin)
1161       vis = InOutputObjVisitor()
1162       for elt in kws:
1163         # fetch real data if necessary
1164         kws[elt] = UnProxyObjectSimple( kws[elt],vis)
1165       self.addInfoOnLevel2("inputHDDMem",vis)
1166       self.context.update(kws)
1167       self.addTimeInfoOnLevel2("endInputTime")
1168     except Exception:
1169       exc_typ,exc_val,exc_fr=sys.exc_info()
1170       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1171       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1172       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode:First %s" % (self.nodeName),0))
1173
1174   def executeSecond(self,outargsname):
1175     """ Same than second part of self.execute to reduce memory peak."""
1176     def executeSecondInternal(monitoringtimeresms):
1177       with GenericPythonMonitoringLauncherCtxMgr( CPUMemoryMonitoring( monitoringtimeresms ) ) as monitoringParams:
1178         currentInstance = self.executeNow( outargsname )
1179         cpumeminfo = ReadCPUMemInfo( monitoringParams )
1180       return cpumeminfo, currentInstance
1181
1182     import sys
1183     try:
1184       self.addTimeInfoOnLevel2("startExecTime")
1185       ##
1186       self.addInfoOnLevel2("measureTimeResolution",self.my_container_py.monitoringtimeresms())
1187       cpumeminfo, self._current_execution_session._current_instance = executeSecondInternal( self.my_container_py.monitoringtimeresms() )
1188       ##
1189       self.addInfoOnLevel2("CPUMemDuringExec",cpumeminfo)
1190       self.addTimeInfoOnLevel2("endExecTime")
1191       self.addTimeInfoOnLevel2("startOutputTime")
1192       argsout=[]
1193       for arg in outargsname:
1194         if arg not in self.context:
1195           raise KeyError("There is no variable %s in context" % arg)
1196         argsout.append(self.context[arg])
1197       ret = [ ]
1198       outputMem = 0
1199       vis = InOutputObjVisitor()
1200       for arg in argsout:
1201         # the proxy mecanism is catched here
1202         argPickle = SpoolPickleObject( arg, vis )
1203         retArg = SenderByte_i( self.poa,argPickle )
1204         id_o = self.poa.activate_object(retArg)
1205         retObj = self.poa.id_to_reference(id_o)
1206         ret.append( retObj._narrow( SALOME.SenderByte ) )
1207         outputMem += len(argPickle)
1208       self.addInfoOnLevel2("outputMem",outputMem)
1209       self.addInfoOnLevel2("outputHDDMem",vis)
1210       self.addTimeInfoOnLevel2("endOutputTime")
1211       self.endOfCurrentExecutionSession()
1212       return ret
1213     except Exception:
1214       exc_typ,exc_val,exc_fr=sys.exc_info()
1215       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1216       print("".join(l)) ; sys.stdout.flush() # print error also in logs of remote container
1217       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode:Second %s, outargsname: %s" % (self.nodeName,outargsname),0))
1218
1219   def listAllVarsInContext(self):
1220       import re
1221       pat = re.compile("^__([a-z]+)__$")
1222       return [elt for elt in self.context if not pat.match(elt) and elt != MY_CONTAINER_ENTRY_IN_GLBS]
1223       
1224   def removeAllVarsInContext(self):
1225       for elt in self.listAllVarsInContext():
1226         del self.context[elt]
1227
1228   def getValueOfVarInContext(self,varName):
1229     try:
1230       return pickle.dumps(self.context[varName],-1)
1231     except Exception:
1232       exc_typ,exc_val,exc_fr=sys.exc_info()
1233       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1234       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1235     pass
1236   
1237   def assignVarInContext(self, varName, value):
1238     try:
1239       self.context[varName][0] = pickle.loads(value)
1240     except Exception:
1241       exc_typ,exc_val,exc_fr=sys.exc_info()
1242       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1243       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1244     pass
1245
1246   def callMethodOnVarInContext(self, varName, methodName, args):
1247     try:
1248       return pickle.dumps( getattr(self.context[varName][0],methodName)(*pickle.loads(args)),-1 )
1249     except Exception:
1250       exc_typ,exc_val,exc_fr=sys.exc_info()
1251       l=traceback.format_exception(exc_typ,exc_val,exc_fr)
1252       raise SALOME.SALOME_Exception(SALOME.ExceptionStruct(SALOME.BAD_PARAM,"".join(l),"PyScriptNode: %s" %self.nodeName,0))
1253     pass
1254
1255   def beginOfCurrentExecutionSession(self):
1256     self._current_execution_session = LogOfCurrentExecutionSession( self._log_script.addExecutionSession() )
1257     self.context[MY_PERFORMANCE_LOG_ENTRY_IN_GLBS] = self._current_execution_session
1258   
1259   def endOfCurrentExecutionSession(self):
1260     self._current_execution_session.finalizeAndPushToMaster()
1261     self._current_execution_session = None
1262
1263   def addInfoOnLevel2(self, key, value):
1264     self._current_execution_session.addInfoOnLevel2(key, value)
1265       
1266   def addTimeInfoOnLevel2(self, key):
1267     from datetime import datetime
1268     self._current_execution_session.addInfoOnLevel2(key,datetime.now())
1269
1270 class PyScriptNode_i(PyScriptNode_Abstract_i):
1271   def __init__(self, nodeName, code, poa, my_container, logscript):
1272     super().__init__(nodeName, code, poa, my_container, logscript)
1273
1274   def executeNow(self, outargsname):
1275     return ExecLocal(self.ccode,self.context,outargsname,self.my_container,self._current_execution_session)
1276     
1277 class PyScriptNode_OutOfProcess_i(PyScriptNode_Abstract_i):
1278   def __init__(self, nodeName, code, poa, my_container, logscript):
1279     super().__init__(nodeName, code, poa, my_container, logscript)
1280
1281   def executeNow(self, outargsname):
1282     return ExecCrashProofWithoutReplay(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1283
1284 class PyScriptNode_OutOfProcess_Replay_i(PyScriptNode_Abstract_i):
1285   def __init__(self, nodeName, code, poa, my_container, logscript):
1286     super().__init__(nodeName, code, poa, my_container, logscript)
1287
1288   def executeNow(self, outargsname):
1289     return ExecCrashProofWithReplay(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1290
1291 class PyScriptNode_OutOfProcess_FT_i(PyScriptNode_Abstract_i):
1292   def __init__(self, nodeName, code, poa, my_container, logscript):
1293     super().__init__(nodeName, code, poa, my_container, logscript)
1294
1295   def executeNow(self, outargsname):
1296     return ExecCrashProofWithoutReplayFT(self.code,self.context,outargsname,self.my_container,self._current_execution_session)
1297
1298 class PyScriptNode_OutOfProcess_Replay_FT_i(PyScriptNode_Abstract_i):
1299   def __init__(self, nodeName, code, poa, my_container, logscript):
1300     super().__init__(nodeName, code, poa, my_container, logscript)
1301
1302   def executeNow(self, outargsname):
1303     return ExecCrashProofWithReplayFT(self.code,self.context,outargsname,self.my_container,self._current_execution_session)