Salome HOME
6d7d73211f837fd94006e9254f551918d94eb4b7
[modules/kernel.git] / src / KERNEL_PY / __init__.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
3 #
4 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
5 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 #
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
11 #
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20 #
21 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 #
23
24 #  File   : salome.py renamed as __init__.py for python packaging (gboulant)
25 #  Author : Paul RASCLE, EDF
26 #  Module : SALOME
27 #
28 """ 
29 Module salome gives access to Salome resources.
30
31 variables:
32
33   - salome.orb             : CORBA
34   - salome.naming_service  : instance of naming Service class
35       - methods:
36           - Resolve(name)  : find a CORBA object (ior) by its pathname
37           - Register(name) : register a CORBA object under a pathname
38
39   - salome.lcc             : instance of lifeCycleCORBA class
40       - methods:
41           - FindOrLoadComponent(server,name) :
42                            obtain an Engine (CORBA object)
43                            or launch the Engine if not found,
44                            with a Server name and an Engine name
45
46   - salome.sg              : salome object to communicate with the graphical user interface (if any)
47       - methods:
48          - updateObjBrowser():
49
50          - SelectedCount():      returns number of selected objects
51          - getSelected(i):       returns entry of selected object number i
52          - getAllSelected():     returns list of entry of selected objects
53          - AddIObject(Entry):    select an existing Interactive object
54          - RemoveIObject(Entry): remove object from selection
55          - ClearIObjects():      clear selection
56
57          - Display(*Entry):
58          - DisplayOnly(Entry):
59          - Erase(Entry):
60          - DisplayAll():
61          - EraseAll():
62
63          - IDToObject(Entry):    returns CORBA reference from entry
64
65   - salome.myStudyName     : active Study Name
66   - salome.myStudy         : the active Study itself (CORBA ior)
67       - methods : defined in SALOMEDS.idl
68
69 """
70 ## @package salome
71 # Module salome gives access to Salome resources.
72 #
73 #  \param salome.orb             : CORBA orb object
74 #  \param salome.naming_service  : instance of naming Service class (SALOME_NamingServicePy::SALOME_NamingServicePy_i)
75 #  \param salome.lcc             : instance of lifeCycleCORBA class (SALOME_LifeCycleCORBA)
76 #  \param salome.sg              : Salome object to communicate with the graphical user interface, if running (see interface in salome_iapp::SalomeOutsideGUI)
77 #  \param salome.myStudyName     : active Study Name
78 #  \param salome.myStudy         : the active Study (interface SALOMEDS::Study)
79
80 #
81 # ==========================================================================
82 #
83 # The function extend_path is used here to aggregate in a single
84 # virtual python package all the python sub-packages embedded in each
85 # SALOME modules (python "namespace" pattern).
86 #
87 ROOT_PYTHONPACKAGE_NAME="salome"
88 #
89 # This root package name is expected to be found as a directory in
90 # some paths of the sys.path variable, especially the paths
91 # <MODULE_ROOT_DIR>/lib/pythonX.Y/site-packages/salome where are
92 # installed the python files. These paths are theorically appended by
93 # the SALOME main runner and should be in the sys.path at this point
94 # of the application. The extend_path is looking then for directories
95 # of the type:
96 #
97 # <MODULE_ROOT_DIR>/lib/pythonX.Y/site-packages/salome/<ROOT_PYTHONPACKAGE_NAME>
98 #
99 # And append them to the sys.path. These directories are supposed to
100 # be the pieces to be aggregated as a single virtual python package.
101 #
102 import os, sys
103 from salome_utils import verbose
104
105 MATCH_ENDING_PATTERN="site-packages" + os.path.sep + "salome"
106
107 def extend_path(pname):
108     for dir in sys.path:
109         if not isinstance(dir, str) or not os.path.isdir(dir) or not dir.endswith(MATCH_ENDING_PATTERN):
110             continue
111         subdir = os.path.join(dir, pname)
112         # XXX This may still add duplicate entries to path on
113         # case-insensitive filesystems
114         if os.path.isdir(subdir) and subdir not in __path__:
115             if verbose(): print("INFO - The directory %s is appended to sys.path" % subdir)
116             __path__.append(subdir)
117
118 extend_path(ROOT_PYTHONPACKAGE_NAME)
119 # ==========================================================================
120 #
121
122 from salome_kernel import *
123 from salome_study import *
124 from salome_iapp import *
125 import salome_study
126
127 #
128 # The next block is workaround for the problem of shared symbols loading for the extension modules (e.g. SWIG-generated)
129 # that causes RTTI unavailable in some cases. To solve this problem, sys.setdlopenflags() function is used.
130 # Depending on the Python version and platform, the dlopen flags can be defined in the dl, DLFUN or ctypes module.
131
132 import sys
133 flags = None
134 if not flags:
135     try:
136         # dl module can be unavailable
137         import dl
138         flags = dl.RTLD_NOW | dl.RTLD_GLOBAL
139     except Exception:
140         pass
141     pass
142 if not flags:
143     try:
144         # DLFCN module can be unavailable
145         import DLFCN
146         flags = DLFCN.RTLD_NOW | DLFCN.RTLD_GLOBAL
147     except Exception:
148         pass
149     pass
150 if not flags:
151     try:
152         # ctypes module can be unavailable
153         import ctypes
154         flags = ctypes.RTLD_GLOBAL
155     except Exception:
156         pass
157     pass
158
159 # Disable -> bug with scipy, seems very dangerous to do that
160 #if flags:
161 #    sys.setdlopenflags(flags)
162 #    pass
163
164 orb, lcc, naming_service, cm, sg, esm, dsm, logm, modulcat, rm = None,None,None,None,None,None,None,None,None,None
165 myStudy, myStudyName = None,None
166
167 salome_initial=True
168
169 def standalone():
170     pass
171
172 def withServers():
173     import KernelBasis
174     KernelBasis.setSSLMode(False)
175
176 def salome_init(path=None, embedded=False, iorfakensfile=None, forced=False):
177     """
178     Initialize SALOME client process (that can also be server).
179     3 modes of initialization exists:
180     - SSL mode (see salome_init_without_session)
181     - SSL mode attached in the context of python execution inside SALOME_Container_No_NS_Serv server (typically YACS)
182     - Classical mode (see salome_init_with_session)
183     :param iorfakensfile: filename inside which IOR of fake NS will be written
184     :param forced: tell if the multi-initialization protection mecanism of salome_init must be skiped of not
185                    (typically in the context where a path to a study is given whereas a previous initialisation without it was done)
186     """
187     if not forced:
188         if lcc is not None:# multi-initialization protection mecanism is based on lcc global var
189             return
190     PATH_TO_STUDY_FILE_TO_INITIATE = "PATH_TO_STUDY_FILE_TO_INITIATE"
191     import KernelBasis
192     if KernelBasis.getSSLMode():
193         if KernelBasis.getIOROfEmbeddedNS() == "":
194             import os
195             # make runSalome.py -t study.hdf toto.py
196             if path is None and PATH_TO_STUDY_FILE_TO_INITIATE in os.environ:
197                 path = os.environ[PATH_TO_STUDY_FILE_TO_INITIATE]
198             salome_init_without_session(path, embedded, iorfakensfile)
199         else:
200             salome_init_without_session_attached(path, embedded)
201     else:
202         salome_init_with_session(path, embedded)
203
204 def salome_init_without_session_common(path=None, embedded=False):
205     from ORBConfigFile import writeORBConfigFileSSL
206     OMNIORB_USER_PATH = "OMNIORB_USER_PATH"
207     def RemoveOmniorbConfigFile():
208         import os
209         if "OMNIORB_CONFIG" in os.environ:
210             fileToRemove = os.environ["OMNIORB_CONFIG"]
211             if os.path.exists(fileToRemove):
212                 os.unlink(fileToRemove)
213
214     if OMNIORB_USER_PATH in os.environ:
215         import atexit
216         writeORBConfigFileSSL(os.environ[OMNIORB_USER_PATH],kwargs={"with_pid":True})
217         atexit.register(RemoveOmniorbConfigFile)
218
219     global lcc,naming_service,myStudy,myStudyName,orb,modulcat,sg
220     import KernelBasis
221     KernelBasis.setSSLMode(True)
222     import KernelDS
223     myStudy = KernelDS.myStudy()
224     import CORBA
225     orb=CORBA.ORB_init([''])
226     import KernelModuleCatalog
227     import SALOME_ModuleCatalog
228     from salome_kernel import list_of_catalogs_regarding_environement
229     modulcat = KernelModuleCatalog.myModuleCatalog( list_of_catalogs_regarding_environement() )
230     #
231     poa = orb.resolve_initial_references("RootPOA")
232     poaManager = poa._get_the_POAManager()
233     poaManager.activate()
234     #
235     sg = salome_iapp_init(embedded)
236     salome_study_init_without_session(path)
237     #
238     from NamingService import NamingService
239     naming_service = NamingService()
240     myStudyName = myStudy.Name
241
242 def salome_init_without_session(path=None, embedded=False, iorfakensfile=None):
243     """
244     Force creation of all servants needed by SALOME session in the current process.
245     A Fake NamingService is created storing reference of all servants in the current process.
246     """
247     salome_init_without_session_common(path,embedded)
248     global lcc,cm,dsm,esm,rm,logm
249     import KernelLauncher
250     cm = KernelLauncher.myContainerManager()
251     type(cm).SetOverrideEnvForContainersSimple = ContainerManagerSetOverrideEnvForContainersSimple
252     rm = KernelLauncher.myResourcesManager()
253     from LifeCycleCORBA import LifeCycleCORBASSL
254     lcc = LifeCycleCORBASSL()
255     # create a FactoryServer Container servant
256     import KernelContainer
257     KernelContainer.myContainer()
258     # activate poaManager to accept co-localized CORBA calls.
259     from KernelSDS import GetDSMInstance
260     import sys
261     if hasattr(sys, 'argv'):
262       argv = sys.argv
263     else:
264       argv = ['']
265     dsm = GetDSMInstance(argv)
266     # esm inherits from SALOME_CPythonHelper singleton already initialized by GetDSMInstance
267     # esm inherits also from SALOME_ResourcesManager creation/initialization (concerning SingleThreadPOA POA) when KernelLauncher.GetContainerManager() has been called
268     esm = KernelLauncher.GetExternalServer()
269     # idem for logm
270     logm = KernelLauncher.myLogManager()
271     type(logm).NaiveFetch = LogManagerNaiveFetch
272     type(logm).Fetch = LogManagerFetch
273     type(logm).DumpInFile = LogManagerDumpInFile
274     type(logm).LaunchMonitoringDumpFile = LogManagerLaunchMonitoringDumpFile
275     type(logm).FinalFetchBeforeDying = LogManagerFinalFetchBeforeDying
276     type(logm).GetLatestMonitoringDumpFile = LogManagerGetLatestMonitoringDumpFile
277     type(logm).DumpIORInFile = LogManagerDumpIORInFile
278     #
279     import KernelLogger
280     naming_service.Register(KernelLogger.myLogger(),"/Logger")
281     #
282     from NamingService import NamingService
283     if iorfakensfile is not None:
284         with open(iorfakensfile,"w") as iorfakensf:
285             iorfakensf.write(NamingService.IOROfNS())
286     
287 CM_NAME_IN_NS = "/ContainerManager"
288 RM_NAME_IN_NS = "/ResourcesManager"
289 DSM_NAME_IN_NS = "/DataServerManager"
290 ESM_NAME_IN_NS = "/ExternalServers"
291 LOGM_NAME_IN_NS = "/LogManager"
292
293 def salome_init_without_session_attached(path=None, embedded=False):
294     """
295     Configuration SSL inside a python interpretor launched in the SALOME_Container_No_NS_Serv.
296     In this configuration, a local FakeNamingService is created and remote objects are stored in it.
297     lcc is pointing to the FakeNamingService above.
298     """
299     salome_init_without_session_common(path,embedded)
300     global lcc,cm,dsm,esm,rm,logm
301     import CORBA
302     orb=CORBA.ORB_init([''])
303     import Engines
304     import KernelBasis
305     nsAbroad = orb.string_to_object( KernelBasis.getIOROfEmbeddedNS() )
306     import SALOME
307     cm = orb.string_to_object( nsAbroad.Resolve(CM_NAME_IN_NS).decode() )
308     type(cm).SetOverrideEnvForContainersSimple = ContainerManagerSetOverrideEnvForContainersSimple
309     naming_service.Register(cm,CM_NAME_IN_NS)
310     rm = orb.string_to_object( nsAbroad.Resolve(RM_NAME_IN_NS).decode() )
311     naming_service.Register(rm,RM_NAME_IN_NS)
312     #
313     from LifeCycleCORBA import LifeCycleCORBASSL
314     lcc = LifeCycleCORBASSL()
315     dsm = orb.string_to_object( nsAbroad.Resolve(DSM_NAME_IN_NS).decode() )
316     naming_service.Register(dsm,DSM_NAME_IN_NS)
317     #
318     esm = orb.string_to_object( nsAbroad.Resolve(ESM_NAME_IN_NS).decode() )
319     naming_service.Register(esm,ESM_NAME_IN_NS)
320     #
321     logm = orb.string_to_object( nsAbroad.Resolve(LOGM_NAME_IN_NS).decode() )
322     naming_service.Register(logm,LOGM_NAME_IN_NS)
323
324 def salome_init_with_session(path=None, embedded=False):
325     """
326     Performs only once SALOME general purpose initialisation for scripts.
327     Provides:
328     orb             reference to CORBA
329     lcc             a LifeCycleCorba instance
330     naming_service  a naming service instance
331     cm              reference to the container manager
332     esm             reference to external server manager
333     dsm             reference to shared dataserver manager
334     modulcat        reference to modulecatalog instance
335     sg              access to SALOME GUI (when linked with IAPP GUI)
336     myStudy         active study itself (CORBA reference)
337     myStudyName     active study name
338     """
339     global salome_initial
340     global orb, lcc, naming_service, cm, esm, dsm, modulcat
341     global sg
342     global myStudy, myStudyName
343     import KernelBasis
344     KernelBasis.setSSLMode(False)
345     try:
346         if salome_initial:
347             salome_initial=False
348             sg = salome_iapp_init(embedded)
349             orb, lcc, naming_service, cm, esm, dsm, modulcat = salome_kernel_init()
350             myStudy, myStudyName = salome_study_init(path)
351             pass
352         pass
353     except RuntimeError as inst:
354         # wait a little to avoid trace mix
355         import time
356         time.sleep(0.2)
357         x = inst
358         print("salome.salome_init_with_session():", x)
359         print("""
360         ============================================
361         May be there is no running SALOME session
362         salome.salome_init() is intended to be used
363         within an already running session
364         ============================================
365         """)
366         raise
367     
368 def salome_close():
369     global salome_initial, myStudy, myStudyName, lcc
370     try:
371         # study can be clear either from GUI or directly with salome.myStudy.Clear()
372         myStudy.Clear()
373     except Exception:
374         pass
375     salome_initial=True
376     salome_iapp_close()
377     salome_study_close()
378     myStudy, myStudyName = None, None
379     lcc = None # to salome_init to rebuild all in case of salome_init after salome_close
380     import KernelBasis
381     if KernelBasis.getSSLMode() and not KernelBasis.getGUIMode():
382         import KernelDS
383         KernelDS.KillGlobalSessionInstance()
384         import KernelSDS
385         KernelSDS.KillCPythonHelper()
386     pass
387
388 def salome_NS():
389     import CORBA
390     import CosNaming
391     orb = CORBA.ORB_init()
392     ns0 = orb.resolve_initial_references("NameService")
393     return ns0._narrow(CosNaming.NamingContext)
394
395 def salome_walk_on_containers(ns,root):
396     import CosNaming
397     it = ns.list(0)[1]
398     if not it:
399         return
400     cont = True
401     while cont:
402         cont,obj = it.next_one()
403         if cont:
404             if obj.binding_name[0].kind == "object":
405                 import Engines
406                 corbaObj = ns.resolve(obj.binding_name)
407                 if isinstance(corbaObj,Engines._objref_Container):
408                     yield corbaObj,(root,obj.binding_name[0].id)
409             else:
410                 father = ns.resolve([obj.binding_name[0]])
411                 for elt,elt2 in salome_walk_on_containers(father,root+[obj.binding_name[0].id]):
412                     yield elt,elt2
413             pass
414         pass
415     pass
416
417 def salome_shutdown_containers_with_session():
418     salome_init()
419     ns=salome_NS()
420     li = [elt for elt in salome_walk_on_containers(ns,[""])]
421     print("Number of containers in NS : {}".format(len(li)))
422     for cont,(root,cont_name) in li:
423         try:
424             cont.Shutdown()
425         except Exception:
426             pass
427         ref_in_ns = "/".join(root+[cont_name])
428         naming_service.Destroy_Name(ref_in_ns)
429     print("Number of containers in NS after clean : {}".format( len( list(salome_walk_on_containers(ns,[""])) )))
430
431 def retrieve_containers_in_ns():
432     return [elt for elt in naming_service.repr() if "/Containers/" == elt[:12]]
433
434 def get_all_containers():
435     containersEntries = retrieve_containers_in_ns()
436     return [naming_service.Resolve(containerEntry) for containerEntry in containersEntries]
437     
438 def salome_shutdown_containers_without_session():
439     for cont in get_all_containers():
440         try:
441             cont.Shutdown()
442         except:
443             pass
444
445 def salome_shutdown_containers():
446     import KernelBasis
447     if KernelBasis.getSSLMode():
448         salome_shutdown_containers_without_session()
449     else:
450         salome_shutdown_containers_with_session()
451
452 class SessionContextManager:
453     def __enter__(self):
454         standalone()
455         salome_init()
456     def __exit__(self, type, value, traceback):
457         salome_close()
458
459 def ContainerManagerSetOverrideEnvForContainersSimple(self,env):
460     envEff = [ Engines.KeyValPairString(key=k,val=v) for k,v in env ]
461     return self.SetOverrideEnvForContainers( envEff )
462
463 def LogManagerNaiveFetch(self):
464     """
465     Fetch data from server with multiple CORBA invokations.
466     """
467     import SALOME_ContainerHelper
468     return [SALOME_ContainerHelper.ContainerLogInfoClt(elt) for elt in self.listOfContainerLogs()]
469
470 def LogManagerFetch(self,clearMemory = False):
471     """
472     Fetch data from server in one shot mode.
473     """
474     from SALOME_ContainerHelper import unserializeLogManager
475     return unserializeLogManager( self.getAllStruct(clearMemory) )
476
477 def LogManagerDumpInFile(self,fileName,clearMemory = False):
478     with open(fileName,"wb") as f:
479         f.write( self.getAllStruct( clearMemory ) )
480
481
482 class LogManagerLaunchMonitoringFileCtxMgr:
483     def __init__(self, intervalInMs, outFileName):
484         self._interval_in_ms = intervalInMs
485         self._out_filename = outFileName
486         self._monitoring_params = None
487     def __enter__(self):
488         import salome
489         self._monitoring_params = salome.logm.LaunchMonitoringDumpFile(self._interval_in_ms, self._out_filename)
490         return self._monitoring_params
491     def __exit__(self,exctype, exc, tb):
492         import SALOME_PyNode
493         import salome
494         SALOME_PyNode.StopMonitoring( self._monitoring_params )
495         salome.logm.GetLatestMonitoringDumpFile()
496         pass
497
498 def LogManagerLaunchMonitoringDumpFile(self, intervalInMs, outFileName):
499     """
500     This method loops indefinitely every intervalInMs milliseconds to dump the singleton 
501     content of perf log stored in salome.logm.
502     This method runs in a dedicated subprocess that can be killed at any time.
503     So subprocess code must deal with.
504
505     See also LogManagerGetLatestMonitoringDumpFile
506     """
507     global orb,logm
508     ior = orb.object_to_string( logm )
509     import os
510     outFileName2 = os.path.abspath( os.path.expanduser(outFileName) )
511     import tempfile
512     import logging
513     import SALOME_PyNode
514     import KernelBasis
515     # outFileNameSave stores the content of outFileName during phase of dumping
516     with tempfile.NamedTemporaryFile(prefix=os.path.basename(outFileName2),dir=os.path.dirname(outFileName2)) as f:
517       outFileNameSave = f.name
518     with tempfile.NamedTemporaryFile(prefix="htopmain_",suffix=".py") as f:
519       tempPyFile = f.name
520     with open(tempPyFile,"w") as f:
521         f.write("""import Engines
522 import os
523 import shutil
524 import CORBA
525 import time
526 orb=CORBA.ORB_init([''], CORBA.ORB_ID)
527 logm = orb.string_to_object("{ior}")
528 outFileName = "{outFileName}"
529 outFileNameSave = "{outFileNameSave}"
530 logm.setFileNamePairOfLogger(outFileName, outFileNameSave )
531 import salome
532 while(True):
533   if os.path.exists( outFileName ):
534     shutil.copy(outFileName,outFileNameSave)
535     logm.versionB_IsTheLatestValidVersion()
536   salome.LogManagerDumpInFile(logm,outFileName)
537   logm.versionA_IsTheLatestValidVersion()
538   time.sleep( {intervalInMs} / 1000.0 )
539 """.format( **locals()))
540     logging.debug( "File for monitoring dump file : {}".format(tempPyFile) )
541     pyFileName = SALOME_PyNode.FileDeleter( tempPyFile )
542     pid = KernelBasis.LaunchMonitoring( tempPyFile )
543     return SALOME_PyNode.MonitoringInfo(pyFileName,intervalInMs,None,pid)
544
545 def LogManagerDumpIORInFile(self, iorFileName):
546     global logm
547     with open(iorFileName,"w") as f:
548         f.write( orb.object_to_string( logm ) )
549
550 def LogManagerLoadFromFile(fileName):
551     from SALOME_ContainerHelper import unserializeLogManager
552     with open(fileName,"rb") as f:
553         data = f.read()
554     return unserializeLogManager( data )
555
556 def LogManagerLoadFromIORFile( iorFile ):
557     global orb
558     def LoadAndWrite(logm,tempFileName):
559         import SALOME_PyNode
560         logm.putStructInFileAtomic( False, tempFileName )
561         tempFileAuto = SALOME_PyNode.FileDeleter( tempFileName )
562         ret = LogManagerLoadFromFile( tempFileAuto.filename )
563         return ret
564     with open(iorFile,"r") as f:
565         ior = f.read()
566     import Engines
567     import tempfile
568     salome_init_without_session()
569     logm = orb.string_to_object( ior )
570     with tempfile.NamedTemporaryFile(dir=os.path.expanduser("~")) as f:
571         tempFileName = f.name
572     return LoadAndWrite( logm, tempFileName )
573
574 def LogManagerFinalFetchBeforeDying(self):
575     import shutil
576     a,b = self.getFileNamePairOfLogger()
577     self.DumpInFile( b )
578     shutil.move( b, a)
579
580 def LogManagerGetLatestMonitoringDumpFile(self):
581     import shutil
582     import logging
583     a,b = self.getFileNamePairOfLogger()
584     if a=="" or b=="":
585         return ""
586     if a == b:
587         return a
588     lastVersion = self.getLastVersionOfFileNameLogger()
589     if lastVersion == a:
590         logging.debug("LogManagerGetLatestMonitoringDumpFile SITUATION A")
591         if os.path.exists( b ):
592             os.remove( b )
593         self.FinalFetchBeforeDying()
594         return a
595     if lastVersion == b:
596         logging.debug("LogManagerGetLatestMonitoringDumpFile SITUATION B")
597         if os.path.exists( b ):
598             shutil.move( b, a)
599         self.FinalFetchBeforeDying()
600         return a
601     logging.warning("in LogManagerGetLatestMonitoringDumpFile an unexpected situation araises.")
602     return ""
603
604 #to expose all objects to pydoc
605 __all__ = dir()