Salome HOME
Management of double foreach and management of proxyfile lifecycle
[modules/kernel.git] / src / Container / SALOME_PyNode.py
index 998e61d8f06dd0e5282c7b60a7a72bf45174a5bd..9274160b86aaad48cb4cb543529d26958afc52c1 100644 (file)
@@ -1,5 +1,5 @@
 #  -*- coding: iso-8859-1 -*-
-# Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
+# Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -121,6 +121,43 @@ SALOME_BIG_OBJ_ON_DISK_THRES_VAR = "SALOME_BIG_OBJ_ON_DISK_THRES"
 # default is 50 MB
 SALOME_BIG_OBJ_ON_DISK_THRES_DFT = 50000000
 
+from ctypes import c_int
+TypeCounter = c_int
+
+def GetSizeOfTCnt():
+  return len( bytes(TypeCounter(0) ) )
+
+def GetObjectFromFile(fname):
+  with open(fname,"rb") as f:
+    cntb = f.read( GetSizeOfTCnt() )
+    cnt = TypeCounter.from_buffer_copy( cntb ).value
+    obj = pickle.load(f)
+  return obj,cnt
+
+def DumpInFile(obj,fname):
+  with open(fname,"wb") as f:
+    f.write( bytes( TypeCounter(1) ) )
+    f.write( obj )
+
+def IncrRefInFile(fname):
+  with open(fname,"rb") as f:
+    cntb = f.read( GetSizeOfTCnt() )
+  cnt = TypeCounter.from_buffer_copy( cntb ).value
+  with open(fname,"rb+") as f:
+    f.write( bytes( TypeCounter(cnt+1) ) )
+
+def DecrRefInFile(fname):
+  import os
+  with open(fname,"rb") as f:
+    cntb = f.read( GetSizeOfTCnt() )
+  cnt = TypeCounter.from_buffer_copy( cntb ).value
+  #
+  if cnt == 1:
+    os.unlink( fname )
+  else:
+    with open(fname,"rb+") as f:
+        f.write( bytes( TypeCounter(cnt-1) ) )
+
 def GetBigObjectOnDiskThreshold():
   import os
   if SALOME_BIG_OBJ_ON_DISK_THRES_VAR in os.environ:
@@ -132,7 +169,7 @@ def GetBigObjectDirectory():
   import os
   if SALOME_FILE_BIG_OBJ_DIR not in os.environ:
     raise RuntimeError("An object of size higher than limit detected and no directory specified to dump it in file !")
-  return os.path.expandvars( os.path.expandvars( os.environ[SALOME_FILE_BIG_OBJ_DIR] ) )
+  return os.path.expanduser( os.path.expandvars( os.environ[SALOME_FILE_BIG_OBJ_DIR] ) )
 
 def GetBigObjectFileName():
   """
@@ -151,12 +188,29 @@ class BigObjectOnDiskBase:
     :type objSerialized: bytes
     """
     self._filename = fileName
+    # attribute _destroy is here to tell client side or server side
+    # only client side can be with _destroy set to True. server side due to risk of concurrency
+    # so pickled form of self must be done with this attribute set to False.
     self._destroy = False
     self.__dumpIntoFile(objSerialized)
 
   def getDestroyStatus(self):
     return self._destroy
 
+  def incrRef(self):
+    if self._destroy:
+      IncrRefInFile( self._filename )
+    else:
+      # should never happen !
+      RuntimeError("Invalid call to incrRef !")
+
+  def decrRef(self):
+    if self._destroy:
+      DecrRefInFile( self._filename )
+    else:
+      # should never happen !
+      RuntimeError("Invalid call to decrRef !")
+
   def unlinkOnDestructor(self):
     self._destroy = True
 
@@ -168,20 +222,30 @@ class BigObjectOnDiskBase:
 
   def __del__(self):
     if self._destroy:
-      import os
-      os.unlink( self._filename )
+      DecrRefInFile( self._filename )
 
   def getFileName(self):
     return self._filename
   
   def __dumpIntoFile(self, objSerialized):
-    with open(self._filename,"wb") as f:
-      f.write(objSerialized)
+    DumpInFile( objSerialized, self._filename )
 
   def get(self):
-    import pickle
-    with open(self._filename,"rb") as f:
-      return pickle.load(f)
+    obj, _ = GetObjectFromFile( self._filename )
+    return obj
+
+  def __float__(self):
+    return float( self.get() )
+    
+  def __int__(self):
+    return int( self.get() )
+    
+  def __str__(self):
+    obj = self.get()
+    if isinstance(obj,str):
+        return obj
+    else:
+        raise RuntimeError("Not a string")
       
 class BigObjectOnDisk(BigObjectOnDiskBase):
   def __init__(self, fileName, objSerialized):
@@ -198,6 +262,12 @@ class BigObjectOnDiskListElement(BigObjectOnDiskBase):
     fullObj = BigObjectOnDiskBase.get(self)
     return fullObj[ self._pos ]
     
+  def __getitem__(self, i):
+    return self.get()[i]
+
+  def __len__(self):
+    return len(self.get())
+    
 class BigObjectOnDiskSequence(BigObjectOnDiskBase):
   def __init__(self, length, fileName, objSerialized):
     BigObjectOnDiskBase.__init__(self, fileName, objSerialized)
@@ -232,14 +302,33 @@ def SpoolPickleObject( obj ):
     pickleProxy = pickle.dumps( proxyObj , pickle.HIGHEST_PROTOCOL )
     return pickleProxy
 
-def UnProxyObject( obj ):
+def UnProxyObjectSimple( obj ):
+  """
+  Method to be called in Remote mode. Alterate the obj _status attribute. 
+  Because the slave process does not participate in the reference counting
+  """
   if isinstance(obj,BigObjectOnDiskBase):
     obj.doNotTouchFile()
     return obj.get()
-  if isinstance(obj,list) or isinstance(obj,tuple):
+  elif isinstance( obj, list):
+    retObj = []
+    for elt in obj:
+      retObj.append( UnProxyObjectSimple(elt) )
+    return retObj
+  else:
+    return obj
+
+def UnProxyObjectSimpleLocal( obj ):
+  """
+  Method to be called in Local mode. Do not alterate the PyObj counter
+  """
+  if isinstance(obj,BigObjectOnDiskBase):
+    return obj.get()
+  elif isinstance( obj, list):
+    retObj = []
     for elt in obj:
-      if isinstance(elt,BigObjectOnDiskBase):
-        elt.doNotTouchFile()
+      retObj.append( UnProxyObjectSimpleLocal(elt) )
+    return retObj
   else:
     return obj
     
@@ -342,7 +431,7 @@ class PyScriptNode_i (Engines__POA.PyScriptNode,Generic):
       _,kws=pickle.loads(data)
       for elt in kws:
         # fetch real data if necessary
-        kws[elt] = UnProxyObject( kws[elt] )
+        kws[elt] = UnProxyObjectSimple( kws[elt] )
       self.context.update(kws)
     except Exception:
       exc_typ,exc_val,exc_fr=sys.exc_info()