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