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