Salome HOME
4a40e68de1cdddabf5ccad714033c95255492022
[modules/kernel.git] / bin / salomeContext.py
1 #! /usr/bin/env python3
2 # Copyright (C) 2013-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 import os
22 import sys
23 import logging
24 import configparser
25
26 from parseConfigFile import parseConfigFile
27
28 import tempfile
29 import pickle
30 import subprocess
31 import sys
32 import platform
33
34 from salomeContextUtils import SalomeContextException
35
36 def usage():
37   msg = '''\
38 Usage: salome [command] [options] [--config=<file,folder,...>] [--with-env-modules=<env_module1,env_module2,...>]
39
40 Commands:
41 =========
42     start           Start a new SALOME instance. Start a single SALOME_Session_Server_No_Server
43                     process with environment relevant to the application and hosting all servants in it.
44     context         Initialize SALOME context. Current environment is extended.
45     shell           Initialize SALOME context, attached to the last created SALOME
46                     instance if any, and executes scripts passed as command arguments.
47                     User works in a Shell terminal. SALOME environment is set but
48                     application is not started.
49     test            Run SALOME tests.
50     info            Display some information about SALOME.
51     doc <module(s)> Show online module documentation (if available).
52                     Module names must be separated by blank characters.
53     help            Show this message.
54     remote          run command in SALOME environment from remote call, ssh or rsh.
55     withsession     Start a new SWS SALOME instance with multiple servers hosting all servants.
56     connect         In SWS context, Connect a Python console to the active SALOME instance.
57     kill <port(s)>  In SWS context, Terminate SALOME instances running on given ports for current user.
58                     Port numbers must be separated by blank characters.
59     killall         Terminate *all* SALOME running SWS instances for current user.
60                     Do not start a new one.
61
62 If no command is given, default is start.
63
64 Command options:
65 ================
66     Use salome <command> --help to show help on command. Available for the
67     following commands: start, shell, connect, test, info.
68
69 --config=<file,folder,...>
70 ==========================
71     Initialize SALOME context from a list of context files and/or a list
72     of folders containing context files. The list is comma-separated, without
73     any blank characters.
74
75 --with-env-modules=<env_module1,env_module2,...>
76 ================================================
77     Initialize SALOME context with the provided additional environment modules.
78     The list is comma-separated, without any blank characters.
79 '''
80
81   print(msg)
82 #
83
84 """
85 The SalomeContext class in an API to configure SALOME context then
86 start SALOME using a single python command.
87
88 """
89 class SalomeContext:
90   """
91   Initialize context from a list of configuration files
92   identified by their names.
93   These files should be in appropriate .cfg format.
94   """
95   def __init__(self, configFileNames=0):
96     self.getLogger().setLevel(logging.INFO)
97     #it could be None explicitly (if user use multiples setVariable...for standalone)
98     if configFileNames is None:
99        return
100     configFileNames = configFileNames or []
101     if len(configFileNames) == 0:
102       raise SalomeContextException("No configuration files given")
103
104     reserved=['PATH', 'DYLD_FALLBACK_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH', 'INCLUDE', 'LIBPATH', 'SALOME_PLUGINS_PATH', 'LIBRARY_PATH', 'QT_PLUGIN_PATH']
105     for filename in configFileNames:
106       basename, extension = os.path.splitext(filename)
107       if extension == ".cfg":
108         self.__setContextFromConfigFile(filename, reserved)
109       else:
110         self.getLogger().error("Unrecognized extension for configuration file: %s", filename)
111   #
112
113   def __loadEnvModules(self, env_modules):
114     modulecmd = os.getenv('LMOD_CMD')
115     if not modulecmd:
116       raise SalomeContextException("Module environment not present")
117       return
118     try:
119       out, err = subprocess.Popen([modulecmd, "python", "try-load"] + env_modules, stdout=subprocess.PIPE).communicate()
120       exec(out)  # define specific environment variables
121     except Exception:
122       raise SalomeContextException("Failed to load env modules: %s ..." % ' '.join(env_modules))
123       pass
124   #
125
126   def runSalome(self, args):
127     import os
128     # Run this module as a script, in order to use appropriate Python interpreter
129     # according to current path (initialized from context files).
130     env_modules_option = "--with-env-modules="
131     env_modules_l = [x for x in args if x.startswith(env_modules_option)]
132     if env_modules_l:
133       env_modules = env_modules_l[-1][len(env_modules_option):].split(',')
134       self.__loadEnvModules(env_modules)
135       args = [x for x in args if not x.startswith(env_modules_option)]
136     else:
137       env_modules = os.getenv("SALOME_ENV_MODULES", None)
138       if env_modules:
139         self.__loadEnvModules(env_modules.split(','))
140
141     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
142     env_copy = os.environ.copy()
143     selfBytes= pickle.dumps(self, protocol=0)
144     argsBytes= pickle.dumps(args, protocol=0)
145     proc = subprocess.Popen(['python3', os.path.join(absoluteAppliPath,"bin","salome","salomeContext.py"), selfBytes.decode('latin1'), argsBytes.decode('latin1')], shell=False, close_fds=True, env=env_copy)
146     out, err = proc.communicate()
147     return out, err, proc.returncode
148   #
149
150   """Append value to PATH environment variable"""
151   def addToPath(self, value):
152     self.addToVariable('PATH', value)
153   #
154
155   """Append value to LD_LIBRARY_PATH environment variable"""
156   def addToLdLibraryPath(self, value):
157     if sys.platform == 'win32':
158       self.addToVariable('PATH', value)
159     elif  sys.platform == 'darwin':
160       if "LAPACK" in value:
161         self.addToVariable('DYLD_FALLBACK_LIBRARY_PATH', value)
162       else:
163         self.addToVariable('DYLD_LIBRARY_PATH', value)
164     else:
165       self.addToVariable('LD_LIBRARY_PATH', value)
166   #
167
168   """Append value to DYLD_LIBRARY_PATH environment variable"""
169   def addToDyldLibraryPath(self, value):
170     self.addToVariable('DYLD_LIBRARY_PATH', value)
171   #
172
173   """Append value to PYTHONPATH environment variable"""
174   def addToPythonPath(self, value):
175     self.addToVariable('PYTHONPATH', value)
176   #
177
178   """Set environment variable to value"""
179   def setVariable(self, name, value, overwrite=False):
180     env = os.getenv(name, '')
181     if env and not overwrite:
182       self.getLogger().error("Environment variable already existing (and not overwritten): %s=%s", name, value)
183       return
184
185     if env:
186       self.getLogger().debug("Overwriting environment variable: %s=%s", name, value)
187
188     value = os.path.expandvars(value) # expand environment variables
189     self.getLogger().debug("Set environment variable: %s=%s", name, value)
190     os.environ[name] = value
191   #
192
193   def setDefaultValue(self, name, value):
194     """ Set environment variable only if it is undefined."""
195     env = os.getenv(name, '')
196     if not env:
197       value = os.path.expandvars(value) # expand environment variables
198       self.getLogger().debug("Set environment variable: %s=%s", name, value)
199       os.environ[name] = value
200
201   """Unset environment variable"""
202   def unsetVariable(self, name):
203     if name in os.environ:
204       del os.environ[name]
205   #
206
207   """Prepend value to environment variable"""
208   def addToVariable(self, name, value, separator=os.pathsep):
209     if value == '':
210       return
211
212     value = os.path.expandvars(value) # expand environment variables
213     self.getLogger().debug("Add to %s: %s", name, value)
214     env = os.getenv(name, None)
215     if env is None:
216       os.environ[name] = value
217     else:
218       os.environ[name] = value + separator + env
219   #
220
221   """Append a variable"""
222   def appendVariable(self, name, value, separator=os.pathsep):
223     if value == '':
224       return
225
226     value = os.path.expandvars(value) # expand environment variables
227     env = os.getenv(name, None)
228     if env is None:
229       os.environ[name] = value
230     else:
231       os.environ[name] = env + separator + value
232     return
233
234   ###################################
235   # This begins the private section #
236   ###################################
237
238   def __parseArguments(self, args):
239     if len(args) == 0 or args[0].startswith("-"):
240       return None, args
241
242     command = args[0]
243     options = args[1:]
244
245     availableCommands = {
246       'start'   : '_sessionless',
247       'withsession' : '_runAppli',
248       'context' : '_setContext',
249       'shell'   : '_runSession',
250       'remote'  : '_runRemote',
251       'connect' : '_runConsole',
252       'kill'    : '_kill',
253       'killall' : '_killAll',
254       'test'    : '_runTests',
255       'info'    : '_showInfo',
256       'doc'     : '_showDoc',
257       'help'    : '_usage',
258       'coffee'  : '_makeCoffee',
259       'car'     : '_getCar',
260       }
261
262     if command not in availableCommands:
263       command = "start"
264       options = args
265
266     return availableCommands[command], options
267   #
268
269   """
270   Run SALOME!
271   Args consist in a mandatory command followed by optional parameters.
272   See usage for details on commands.
273   """
274   def _startSalome(self, args):
275     import os
276     import sys
277     try:
278       from setenv import add_path
279       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
280       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
281       add_path(path, "PYTHONPATH")
282       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
283       add_path(path, "PYTHONPATH")
284
285     except Exception:
286       pass
287
288     command, options = self.__parseArguments(args)
289     sys.argv = options
290
291     # append option from env var SALOME_OPTIONS if it's available
292     if os.getenv("SALOME_OPTIONS"):
293       option_from_env = os.getenv("SALOME_OPTIONS").split(' ')
294       sys.argv += option_from_env
295
296     if command is None:
297       if args and args[0] in ["-h","--help","help"]:
298         usage()
299         return 0
300       # try to default to "start" command
301       command = "_sessionless"
302
303     try:
304       res = getattr(self, command)(options) # run appropriate method
305       return res or 0
306     except SystemExit as ex:
307       if ex.code != 0:
308         self.getLogger().error("SystemExit %s in method %s.", ex.code, command)
309       return ex.code
310     except SalomeContextException as e:
311       self.getLogger().error(e)
312       return 1
313     except Exception:
314       self.getLogger().error("Unexpected error:")
315       import traceback
316       traceback.print_exc()
317       return 1
318   #
319
320   def __setContextFromConfigFile(self, filename, reserved=None):
321     mesa_root_dir = "MESA_ROOT_DIR"
322     if reserved is None:
323       reserved = []
324     try:
325       configInfo = parseConfigFile(filename, reserved)
326       unsetVars = configInfo.unsetVariables
327       configVars = configInfo.outputVariables
328       reservedDict = configInfo.reservedValues
329       defaultValues = configInfo.defaultValues
330     except SalomeContextException as e:
331       msg = "%s"%e
332       self.getLogger().error(msg)
333       return 1
334
335     # unset variables
336     for var in unsetVars:
337       self.unsetVariable(var)
338
339     # mesa stuff
340     if "MESA_GL_VERSION_OVERRIDE" in os.environ:
341       configVarsDict = {k:v for (k,v) in configVars}
342       if mesa_root_dir in configVarsDict:
343         path_to_mesa_lib = os.path.join(configVarsDict[mesa_root_dir],"lib")
344         if os.name == "posix":
345           self.addToVariable("LD_LIBRARY_PATH",path_to_mesa_lib)
346         else:
347           self.addToVariable("PATH",path_to_mesa_lib)
348
349     # set context
350     for reserved in reservedDict:
351       a = [_f for _f in reservedDict[reserved] if _f] # remove empty elements
352       a = [ os.path.realpath(x) for x in a ]
353       reformattedVals = os.pathsep.join(a)
354       if reserved in ["INCLUDE", "LIBPATH"]:
355         self.addToVariable(reserved, reformattedVals, separator=' ')
356       else:
357         self.addToVariable(reserved, reformattedVals)
358       pass
359
360
361     for key,val in configVars:
362       self.setVariable(key, val, overwrite=True)
363       pass
364
365     for key,val in defaultValues:
366       self.setDefaultValue(key, val)
367       pass
368
369     pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
370     pythonpath = [ os.path.realpath(x) for x in pythonpath ]
371     sys.path[:0] = pythonpath
372   #
373
374   def _runAppli(self, args=None):
375     if args is None:
376       args = []
377     # Initialize SALOME environment
378     sys.argv = ['runSalomeOld'] + args
379     import setenv
380     setenv.main(True, exeName="salome withsession")
381
382     import runSalomeOld
383     runSalomeOld.runSalome()
384     return 0
385   #
386
387   def _sessionless(self, args=None):
388     if args is None:
389       args = []
390     sys.argv = ['runSalome'] + args
391     import setenv
392     setenv.main(True, exeName="salome withsession")
393
394     import runSalome
395     runSalome.runSalome()
396     return 0
397   #
398
399   def _setContext(self, args=None):
400     salome_context_set = os.getenv("SALOME_CONTEXT_SET")
401     if salome_context_set:
402       print("***")
403       print("*** SALOME context has already been set.")
404       print("*** Enter 'exit' (only once!) to leave SALOME context.")
405       print("***")
406       return 0
407
408     os.environ["SALOME_CONTEXT_SET"] = "yes"
409     print("***")
410     print("*** SALOME context is now set.")
411     print("*** Enter 'exit' (only once!) to leave SALOME context.")
412     print("***")
413
414     if sys.platform == 'win32':
415       cmd = ['cmd.exe']
416     else:
417       cmd = ["/bin/bash"]
418     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
419     proc.communicate()
420     return proc.returncode
421   #
422
423   def _runSession(self, args=None):
424     if args is None:
425       args = []
426     sys.argv = ['runSession'] + args
427     import runSession
428     params, args = runSession.configureSession(args, exe="salome shell")
429
430     sys.argv = ['runSession'] + args
431     import setenv
432     setenv.main(True)
433
434     return runSession.runSession(params, args)
435   #
436
437   def _runRemote(self, args=None):
438     if args is None:
439       args = []
440 #   complete salome environment 
441     sys.argv = ['runRemote']
442     import setenv
443     setenv.main(True)
444
445     import runRemote
446     return runRemote.runRemote(args)
447   #
448
449   def _runConsole(self, args=None):
450     if args is None:
451       args = []
452     # Initialize SALOME environment
453     sys.argv = ['runConsole']
454     import setenv
455     setenv.main(True)
456
457     import runConsole
458     return runConsole.connect(args)
459   #
460
461   def _kill(self, args=None):
462     if args is None:
463       args = []
464     ports = args
465     if not ports:
466       print("Port number(s) not provided to command: salome kill <port(s)>")
467       return 1
468
469     import subprocess
470     sys.argv = ['kill']
471     import setenv
472     setenv.main(True)
473     if os.getenv("NSHOST") == "no_host":
474       os.unsetenv("NSHOST")
475     for port in ports:
476       if sys.platform == "win32":
477         proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
478       else:
479         proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
480       proc.communicate()
481     return 0
482   #
483
484   def _killAll(self, unused=None):
485     sys.argv = ['killAll']
486     import setenv
487     setenv.main(True)
488     if os.getenv("NSHOST") == "no_host":
489       os.unsetenv("NSHOST")
490     try:
491       import PortManager # mandatory
492       import subprocess
493       ports = PortManager.getBusyPorts()['this']
494
495       if ports:
496         for port in ports:
497           if sys.platform == "win32":
498             proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
499           else:
500             proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
501           proc.communicate()
502     except ImportError:
503       # :TODO: should be declared obsolete
504       from killSalome import killAllPorts
505       killAllPorts()
506       pass
507     from addToKillList import killList
508     killList()
509     return 0
510   #
511
512   def _runTests(self, args=None):
513     if args is None:
514       args = []
515     sys.argv = ['runTests']
516     import setenv
517     setenv.main(True)
518
519     import runTests
520     return runTests.runTests(args, exe="salome test")
521   #
522
523   def _showSoftwareVersions(self, softwares=None):
524     config = configparser.SafeConfigParser()
525     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
526     filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
527     versions = {}
528     max_len = 0
529     with open(filename) as f:
530       for line in f:
531         try:
532           software, version, sha1 = line.split()
533           versions[software.upper()] = version
534           if len(software) > max_len:
535             max_len = len(software)
536         except Exception:
537           pass
538         pass
539       pass
540     if softwares:
541       for soft in softwares:
542         if soft.upper() in versions:
543           print(soft.upper().rjust(max_len), versions[soft.upper()])
544     else:
545       import collections
546       od = collections.OrderedDict(sorted(versions.items()))
547       for name, version in od.items():
548         print(name.rjust(max_len), versions[name])
549     pass
550
551   def _showInfo(self, args=None):
552     if args is None:
553       args = []
554
555     usage = "Usage: salome info [options]"
556     epilog  = """\n
557 Display some information about SALOME.\n
558 Available options are:
559     -p,--ports                     Show the list of busy ports (running SALOME instances).
560     -s,--softwares [software(s)]   Show the list and versions of SALOME softwares.
561                                    Software names must be separated by blank characters.
562                                    If no software is given, show version of all softwares.
563     -v,--version                   Show running SALOME version.
564     -h,--help                      Show this message.
565 """
566     if not args:
567       args = ["--version"]
568
569     if "-h" in args or "--help" in args:
570       print(usage + epilog)
571       return 0
572
573     if "-p" in args or "--ports" in args:
574       import PortManager
575       ports = PortManager.getBusyPorts()
576       this_ports = ports['this']
577       other_ports = ports['other']
578       if this_ports or other_ports:
579           print("SALOME instances are running on the following ports:")
580           if this_ports:
581               print("   This application:", this_ports)
582           else:
583               print("   No SALOME instances of this application")
584           if other_ports:
585               print("   Other applications:", other_ports)
586           else:
587               print("   No SALOME instances of other applications")
588       else:
589           print("No SALOME instances are running")
590
591     if "-s" in args or "--softwares" in args:
592       if "-s" in args:
593         index = args.index("-s")
594       else:
595         index = args.index("--softwares")
596       indexEnd=index+1
597       while indexEnd < len(args) and args[indexEnd][0] != "-":
598         indexEnd = indexEnd + 1
599       self._showSoftwareVersions(softwares=args[index+1:indexEnd])
600
601     if "-v" in args or "--version" in args:
602       print("Running with python", platform.python_version())
603       return self._sessionless(["--version"])
604
605     return 0
606   #
607
608   def _showDoc(self, args=None):
609     if args is None:
610       args = []
611
612     modules = args
613     if not modules:
614       print("Module(s) not provided to command: salome doc <module(s)>")
615       return 1
616
617     appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
618     if not appliPath:
619       raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
620     baseDir = os.path.join(appliPath, "share", "doc", "salome")
621     for module in modules:
622       docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
623       if not os.path.isfile(docfile):
624         docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
625       if not os.path.isfile(docfile):
626         docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
627       if os.path.isfile(docfile):
628         out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
629       else:
630         print("Online documentation is not accessible for module:", module)
631
632   def _usage(self, unused=None):
633     usage()
634   #
635
636   def _makeCoffee(self, unused=None):
637     print("                        (")
638     print("                          )     (")
639     print("                   ___...(-------)-....___")
640     print("               .-\"\"       )    (          \"\"-.")
641     print("         .-\'``\'|-._             )         _.-|")
642     print("        /  .--.|   `\"\"---...........---\"\"`   |")
643     print("       /  /    |                             |")
644     print("       |  |    |                             |")
645     print("        \\  \\   |                             |")
646     print("         `\\ `\\ |                             |")
647     print("           `\\ `|            SALOME           |")
648     print("           _/ /\\            4 EVER           /")
649     print("          (__/  \\             <3            /")
650     print("       _..---\"\"` \\                         /`\"\"---.._")
651     print("    .-\'           \\                       /          \'-.")
652     print("   :               `-.__             __.-\'              :")
653     print("   :                  ) \"\"---...---\"\" (                 :")
654     print("    \'._               `\"--...___...--\"`              _.\'")
655     print("      \\\"\"--..__                              __..--\"\"/")
656     print("       \'._     \"\"\"----.....______.....----\"\"\"     _.\'")
657     print("          `\"\"--..,,_____            _____,,..--\"\"`")
658     print("                        `\"\"\"----\"\"\"`")
659     print("")
660     print("                    SALOME is working for you; what else?")
661     print("")
662   #
663
664   def _getCar(self, unused=None):
665     print("                                              _____________")
666     print("                                  ..---:::::::-----------. ::::;;.")
667     print("                               .\'\"\"\"\"\"\"                  ;;   \\  \":.")
668     print("                            .\'\'                          ;     \\   \"\\__.")
669     print("                          .\'                            ;;      ;   \\\\\";")
670     print("                        .\'                              ;   _____;   \\\\/")
671     print("                      .\'                               :; ;\"     \\ ___:\'.")
672     print("                    .\'--...........................    : =   ____:\"    \\ \\")
673     print("               ..-\"\"                               \"\"\"\'  o\"\"\"     ;     ; :")
674     print("          .--\"\"  .----- ..----...    _.-    --.  ..-\"     ;       ;     ; ;")
675     print("       .\"\"_-     \"--\"\"-----\'\"\"    _-\"        .-\"\"         ;        ;    .-.")
676     print("    .\'  .\'   SALOME             .\"         .\"              ;       ;   /. |")
677     print("   /-./\'         4 EVER <3    .\"          /           _..  ;       ;   ;;;|")
678     print("  :  ;-.______               /       _________==.    /_  \\ ;       ;   ;;;;")
679     print("  ;  / |      \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\"          :    /\" \". |;       ; _; ;;;")
680     print(" /\"-/  |                /   /                  /   /     ;|;      ;-\" | ;\';")
681     print(":-  :   \"\"\"----______  /   /              ____.   .  .\"\'. ;;   .-\"..T\"   .")
682     print("\'. \"  ___            \"\":   \'\"\"\"\"\"\"\"\"\"\"\"\"\"\"    .   ; ;    ;; ;.\" .\"   \'--\"")
683     print(" \",   __ \"\"\"  \"\"---... :- - - - - - - - - \' \'  ; ;  ;    ;;\"  .\"")
684     print("  /. ;  \"\"\"---___                             ;  ; ;     ;|.\"\"")
685     print(" :  \":           \"\"\"----.    .-------.       ;   ; ;     ;:")
686     print("  \\  \'--__               \\   \\        \\     /    | ;     ;;")
687     print("   \'-..   \"\"\"\"---___      :   .______..\\ __/..-\"\"|  ;   ; ;")
688     print("       \"\"--..       \"\"\"--\"        m l s         .   \". . ;")
689     print("             \"\"------...                  ..--\"\"      \" :")
690     print("                        \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"    \\        /")
691     print("                                               \"------\"")
692     print("")
693     print("                                Drive your simulation properly with SALOME!")
694     print("")
695   #
696
697   # Add the following two methods since logger is not pickable
698   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
699   def __getstate__(self):
700     d = dict(self.__dict__)
701     if hasattr(self, '_logger'):
702       del d['_logger']
703     return d
704   #
705   def __setstate__(self, d):
706     self.__dict__.update(d) # I *think* this is a safe way to do it
707   #
708   # Excluding self._logger from pickle operation imply using the following method to access logger
709   def getLogger(self):
710     if not hasattr(self, '_logger'):
711       self._logger = logging.getLogger(__name__)
712       #self._logger.setLevel(logging.DEBUG)
713       #self._logger.setLevel(logging.WARNING)
714       self._logger.setLevel(logging.ERROR)
715     return self._logger
716   #
717
718 if __name__ == "__main__":
719   if len(sys.argv) == 3:
720     context = pickle.loads(sys.argv[1].encode('latin1'))
721     args = pickle.loads(sys.argv[2].encode('latin1'))
722
723     status = context._startSalome(args)
724     sys.exit(status)
725   else:
726     usage()
727 #