]> SALOME platform Git repositories - modules/kernel.git/blob - bin/salomeContext.py
Salome HOME
salomeContext.py: New routine removeFromVariable.
[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   """Remove value from environment variable"""
235   def removeFromVariable(self, name, value, separator=os.pathsep):
236     if value == '':
237       return
238
239     value = os.path.expandvars(value) # expand environment variables
240     self.getLogger().debug("Remove from %s: %s", name, value)
241     env = os.getenv(name, None)
242     if env == value:
243       env = ''
244     else:
245       env = env.removeprefix(value + separator)
246       env = env.removesuffix(separator + value)
247       env = env.replace(separator + value + separator, ':')
248
249     os.environ[name] = env
250   #
251
252   ###################################
253   # This begins the private section #
254   ###################################
255
256   def __parseArguments(self, args):
257     if len(args) == 0 or args[0].startswith("-"):
258       return None, args
259
260     command = args[0]
261     options = args[1:]
262
263     availableCommands = {
264       'start'   : '_sessionless',
265       'withsession' : '_runAppli',
266       'context' : '_setContext',
267       'shell'   : '_runSession',
268       'remote'  : '_runRemote',
269       'connect' : '_runConsole',
270       'kill'    : '_kill',
271       'killall' : '_killAll',
272       'test'    : '_runTests',
273       'info'    : '_showInfo',
274       'doc'     : '_showDoc',
275       'help'    : '_usage',
276       'coffee'  : '_makeCoffee',
277       'car'     : '_getCar',
278       }
279
280     if command not in availableCommands:
281       command = "start"
282       options = args
283
284     return availableCommands[command], options
285   #
286
287   """
288   Run SALOME!
289   Args consist in a mandatory command followed by optional parameters.
290   See usage for details on commands.
291   """
292   def _startSalome(self, args):
293     import os
294     import sys
295     try:
296       from setenv import add_path
297       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
298       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome"))
299       add_path(path, "PYTHONPATH")
300       path = os.path.realpath(os.path.join(absoluteAppliPath, "bin", "salome", "appliskel"))
301       add_path(path, "PYTHONPATH")
302
303     except Exception:
304       pass
305
306     command, options = self.__parseArguments(args)
307     sys.argv = options
308
309     if command is None:
310       if args and args[0] in ["-h","--help","help"]:
311         usage()
312         return 0
313       # try to default to "start" command
314       command = "_sessionless"
315
316     try:
317       res = getattr(self, command)(options) # run appropriate method
318       return res or 0
319     except SystemExit as ex:
320       if ex.code != 0:
321         self.getLogger().error("SystemExit %s in method %s.", ex.code, command)
322       return ex.code
323     except SalomeContextException as e:
324       self.getLogger().error(e)
325       return 1
326     except Exception:
327       self.getLogger().error("Unexpected error:")
328       import traceback
329       traceback.print_exc()
330       return 1
331   #
332
333   def __setContextFromConfigFile(self, filename, reserved=None):
334     mesa_root_dir = "MESA_ROOT_DIR"
335     if reserved is None:
336       reserved = []
337     try:
338       configInfo = parseConfigFile(filename, reserved)
339       unsetVars = configInfo.unsetVariables
340       configVars = configInfo.outputVariables
341       reservedDict = configInfo.reservedValues
342       defaultValues = configInfo.defaultValues
343     except SalomeContextException as e:
344       msg = "%s"%e
345       self.getLogger().error(msg)
346       return 1
347
348     # unset variables
349     for var in unsetVars:
350       self.unsetVariable(var)
351
352     # mesa stuff
353     if "MESA_GL_VERSION_OVERRIDE" in os.environ:
354       configVarsDict = {k:v for (k,v) in configVars}
355       if mesa_root_dir in configVarsDict:
356         path_to_mesa_lib = os.path.join(configVarsDict[mesa_root_dir],"lib")
357         if os.name == "posix":
358           self.addToVariable("LD_LIBRARY_PATH",path_to_mesa_lib)
359         else:
360           self.addToVariable("PATH",path_to_mesa_lib)
361
362     # set context
363     for reserved in reservedDict:
364       a = [_f for _f in reservedDict[reserved] if _f] # remove empty elements
365       a = [ os.path.realpath(x) for x in a ]
366       reformattedVals = os.pathsep.join(a)
367       if reserved in ["INCLUDE", "LIBPATH"]:
368         self.addToVariable(reserved, reformattedVals, separator=' ')
369       else:
370         self.addToVariable(reserved, reformattedVals)
371       pass
372
373
374     for key,val in configVars:
375       self.setVariable(key, val, overwrite=True)
376       pass
377
378     for key,val in defaultValues:
379       self.setDefaultValue(key, val)
380       pass
381
382     pythonpath = os.getenv('PYTHONPATH','').split(os.pathsep)
383     pythonpath = [ os.path.realpath(x) for x in pythonpath ]
384     sys.path[:0] = pythonpath
385   #
386
387   def _runAppli(self, args=None):
388     if args is None:
389       args = []
390     # Initialize SALOME environment
391     sys.argv = ['runSalomeOld'] + args
392     import setenv
393     setenv.main(True, exeName="salome withsession")
394
395     import runSalomeOld
396     runSalomeOld.runSalome()
397     return 0
398   #
399
400   def _sessionless(self, args=None):
401     if args is None:
402       args = []
403     sys.argv = ['runSalome'] + args
404     import setenv
405     setenv.main(True, exeName="salome withsession")
406
407     import runSalome
408     runSalome.runSalome()
409     return 0
410   #
411
412   def _setContext(self, args=None):
413     salome_context_set = os.getenv("SALOME_CONTEXT_SET")
414     if salome_context_set:
415       print("***")
416       print("*** SALOME context has already been set.")
417       print("*** Enter 'exit' (only once!) to leave SALOME context.")
418       print("***")
419       return 0
420
421     os.environ["SALOME_CONTEXT_SET"] = "yes"
422     print("***")
423     print("*** SALOME context is now set.")
424     print("*** Enter 'exit' (only once!) to leave SALOME context.")
425     print("***")
426
427     if sys.platform == 'win32':
428       cmd = ['cmd.exe']
429     else:
430       cmd = ["/bin/bash"]
431     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
432     proc.communicate()
433     return proc.returncode
434   #
435
436   def _runSession(self, args=None):
437     if args is None:
438       args = []
439     sys.argv = ['runSession'] + args
440     import runSession
441     params, args = runSession.configureSession(args, exe="salome shell")
442
443     sys.argv = ['runSession'] + args
444     import setenv
445     setenv.main(True)
446
447     return runSession.runSession(params, args)
448   #
449
450   def _runRemote(self, args=None):
451     if args is None:
452       args = []
453 #   complete salome environment
454     sys.argv = ['runRemote']
455     import setenv
456     setenv.main(True)
457
458     import runRemote
459     return runRemote.runRemote(args)
460   #
461
462   def _runConsole(self, args=None):
463     if args is None:
464       args = []
465     # Initialize SALOME environment
466     sys.argv = ['runConsole']
467     import setenv
468     setenv.main(True)
469
470     import runConsole
471     return runConsole.connect(args)
472   #
473
474   def _kill(self, args=None):
475     if args is None:
476       args = []
477     ports = args
478     if not ports:
479       print("Port number(s) not provided to command: salome kill <port(s)>")
480       return 1
481
482     import subprocess
483     sys.argv = ['kill']
484     import setenv
485     setenv.main(True)
486     if os.getenv("NSHOST") == "no_host":
487       os.unsetenv("NSHOST")
488     for port in ports:
489       if sys.platform == "win32":
490         proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
491       else:
492         proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
493       proc.communicate()
494     return 0
495   #
496
497   def _killAll(self, unused=None):
498     sys.argv = ['killAll']
499     import setenv
500     setenv.main(True)
501     if os.getenv("NSHOST") == "no_host":
502       os.unsetenv("NSHOST")
503     try:
504       import PortManager # mandatory
505       import subprocess
506       ports = PortManager.getBusyPorts()['this']
507
508       if ports:
509         for port in ports:
510           if sys.platform == "win32":
511             proc = subprocess.Popen([os.getenv("PYTHONBIN"), "-m", "killSalomeWithPort", str(port)])
512           else:
513             proc = subprocess.Popen(["killSalomeWithPort.py", str(port)])
514           proc.communicate()
515     except ImportError:
516       # :TODO: should be declared obsolete
517       from killSalome import killAllPorts
518       killAllPorts()
519       pass
520     from addToKillList import killList
521     killList()
522     return 0
523   #
524
525   def _runTests(self, args=None):
526     if args is None:
527       args = []
528     sys.argv = ['runTests']
529     import setenv
530     setenv.main(True)
531
532     import runTests
533     return runTests.runTests(args, exe="salome test")
534   #
535
536   def _showSoftwareVersions(self, softwares=None):
537     config = configparser.SafeConfigParser()
538     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH')
539     filename = os.path.join(absoluteAppliPath, "sha1_collections.txt")
540     versions = {}
541     max_len = 0
542     with open(filename) as f:
543       for line in f:
544         try:
545           software, version, sha1 = line.split()
546           versions[software.upper()] = version
547           if len(software) > max_len:
548             max_len = len(software)
549         except Exception:
550           pass
551         pass
552       pass
553     if softwares:
554       for soft in softwares:
555         if soft.upper() in versions:
556           print(soft.upper().rjust(max_len), versions[soft.upper()])
557     else:
558       import collections
559       od = collections.OrderedDict(sorted(versions.items()))
560       for name, version in od.items():
561         print(name.rjust(max_len), versions[name])
562     pass
563
564   def _showInfo(self, args=None):
565     if args is None:
566       args = []
567
568     usage = "Usage: salome info [options]"
569     epilog  = """\n
570 Display some information about SALOME.\n
571 Available options are:
572     -p,--ports                     Show the list of busy ports (running SALOME instances).
573     -s,--softwares [software(s)]   Show the list and versions of SALOME softwares.
574                                    Software names must be separated by blank characters.
575                                    If no software is given, show version of all softwares.
576     -v,--version                   Show running SALOME version.
577     -h,--help                      Show this message.
578 """
579     if not args:
580       args = ["--version"]
581
582     if "-h" in args or "--help" in args:
583       print(usage + epilog)
584       return 0
585
586     if "-p" in args or "--ports" in args:
587       import PortManager
588       ports = PortManager.getBusyPorts()
589       this_ports = ports['this']
590       other_ports = ports['other']
591       if this_ports or other_ports:
592           print("SALOME instances are running on the following ports:")
593           if this_ports:
594               print("   This application:", this_ports)
595           else:
596               print("   No SALOME instances of this application")
597           if other_ports:
598               print("   Other applications:", other_ports)
599           else:
600               print("   No SALOME instances of other applications")
601       else:
602           print("No SALOME instances are running")
603
604     if "-s" in args or "--softwares" in args:
605       if "-s" in args:
606         index = args.index("-s")
607       else:
608         index = args.index("--softwares")
609       indexEnd=index+1
610       while indexEnd < len(args) and args[indexEnd][0] != "-":
611         indexEnd = indexEnd + 1
612       self._showSoftwareVersions(softwares=args[index+1:indexEnd])
613
614     if "-v" in args or "--version" in args:
615       print("Running with python", platform.python_version())
616       return self._sessionless(["--version"])
617
618     return 0
619   #
620
621   def _showDoc(self, args=None):
622     if args is None:
623       args = []
624
625     modules = args
626     if not modules:
627       print("Module(s) not provided to command: salome doc <module(s)>")
628       return 1
629
630     appliPath = os.getenv("ABSOLUTE_APPLI_PATH")
631     if not appliPath:
632       raise SalomeContextException("Unable to find application path. Please check that the variable ABSOLUTE_APPLI_PATH is set.")
633     baseDir = os.path.join(appliPath, "share", "doc", "salome")
634     for module in modules:
635       docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
636       if not os.path.isfile(docfile):
637         docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
638       if not os.path.isfile(docfile):
639         docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
640       if os.path.isfile(docfile):
641         out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
642       else:
643         print("Online documentation is not accessible for module:", module)
644
645   def _usage(self, unused=None):
646     usage()
647   #
648
649   def _makeCoffee(self, unused=None):
650     print("                        (")
651     print("                          )     (")
652     print("                   ___...(-------)-....___")
653     print("               .-\"\"       )    (          \"\"-.")
654     print("         .-\'``\'|-._             )         _.-|")
655     print("        /  .--.|   `\"\"---...........---\"\"`   |")
656     print("       /  /    |                             |")
657     print("       |  |    |                             |")
658     print("        \\  \\   |                             |")
659     print("         `\\ `\\ |                             |")
660     print("           `\\ `|            SALOME           |")
661     print("           _/ /\\            4 EVER           /")
662     print("          (__/  \\             <3            /")
663     print("       _..---\"\"` \\                         /`\"\"---.._")
664     print("    .-\'           \\                       /          \'-.")
665     print("   :               `-.__             __.-\'              :")
666     print("   :                  ) \"\"---...---\"\" (                 :")
667     print("    \'._               `\"--...___...--\"`              _.\'")
668     print("      \\\"\"--..__                              __..--\"\"/")
669     print("       \'._     \"\"\"----.....______.....----\"\"\"     _.\'")
670     print("          `\"\"--..,,_____            _____,,..--\"\"`")
671     print("                        `\"\"\"----\"\"\"`")
672     print("")
673     print("                    SALOME is working for you; what else?")
674     print("")
675   #
676
677   def _getCar(self, unused=None):
678     print("                                              _____________")
679     print("                                  ..---:::::::-----------. ::::;;.")
680     print("                               .\'\"\"\"\"\"\"                  ;;   \\  \":.")
681     print("                            .\'\'                          ;     \\   \"\\__.")
682     print("                          .\'                            ;;      ;   \\\\\";")
683     print("                        .\'                              ;   _____;   \\\\/")
684     print("                      .\'                               :; ;\"     \\ ___:\'.")
685     print("                    .\'--...........................    : =   ____:\"    \\ \\")
686     print("               ..-\"\"                               \"\"\"\'  o\"\"\"     ;     ; :")
687     print("          .--\"\"  .----- ..----...    _.-    --.  ..-\"     ;       ;     ; ;")
688     print("       .\"\"_-     \"--\"\"-----\'\"\"    _-\"        .-\"\"         ;        ;    .-.")
689     print("    .\'  .\'   SALOME             .\"         .\"              ;       ;   /. |")
690     print("   /-./\'         4 EVER <3    .\"          /           _..  ;       ;   ;;;|")
691     print("  :  ;-.______               /       _________==.    /_  \\ ;       ;   ;;;;")
692     print("  ;  / |      \"\"\"\"\"\"\"\"\"\"\".---.\"\"\"\"\"\"\"          :    /\" \". |;       ; _; ;;;")
693     print(" /\"-/  |                /   /                  /   /     ;|;      ;-\" | ;\';")
694     print(":-  :   \"\"\"----______  /   /              ____.   .  .\"\'. ;;   .-\"..T\"   .")
695     print("\'. \"  ___            \"\":   \'\"\"\"\"\"\"\"\"\"\"\"\"\"\"    .   ; ;    ;; ;.\" .\"   \'--\"")
696     print(" \",   __ \"\"\"  \"\"---... :- - - - - - - - - \' \'  ; ;  ;    ;;\"  .\"")
697     print("  /. ;  \"\"\"---___                             ;  ; ;     ;|.\"\"")
698     print(" :  \":           \"\"\"----.    .-------.       ;   ; ;     ;:")
699     print("  \\  \'--__               \\   \\        \\     /    | ;     ;;")
700     print("   \'-..   \"\"\"\"---___      :   .______..\\ __/..-\"\"|  ;   ; ;")
701     print("       \"\"--..       \"\"\"--\"        m l s         .   \". . ;")
702     print("             \"\"------...                  ..--\"\"      \" :")
703     print("                        \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"    \\        /")
704     print("                                               \"------\"")
705     print("")
706     print("                                Drive your simulation properly with SALOME!")
707     print("")
708   #
709
710   # Add the following two methods since logger is not pickable
711   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
712   def __getstate__(self):
713     d = dict(self.__dict__)
714     if hasattr(self, '_logger'):
715       del d['_logger']
716     return d
717   #
718   def __setstate__(self, d):
719     self.__dict__.update(d) # I *think* this is a safe way to do it
720   #
721   # Excluding self._logger from pickle operation imply using the following method to access logger
722   def getLogger(self):
723     if not hasattr(self, '_logger'):
724       self._logger = logging.getLogger(__name__)
725       #self._logger.setLevel(logging.DEBUG)
726       #self._logger.setLevel(logging.WARNING)
727       self._logger.setLevel(logging.ERROR)
728     return self._logger
729   #
730
731 if __name__ == "__main__":
732   if len(sys.argv) == 3:
733     context = pickle.loads(sys.argv[1].encode('latin1'))
734     args = pickle.loads(sys.argv[2].encode('latin1'))
735
736     status = context._startSalome(args)
737     sys.exit(status)
738   else:
739     usage()
740 #