Salome HOME
merge from master 8B7B
[tools/sat.git] / src / salomeTools.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 """
20 This file is the main API file for salomeTools
21
22 | Warning: NO '__main__ ' call allowed,
23 |          Use 'sat' (in parent directory)
24 |
25 | Usage: see file ../sat
26 """
27
28 import sys
29
30 # exit OKSYS and KOSYS seems equal on linux or windows
31 _OKSYS = 0  # OK
32 _KOSYS = 1  # KO
33
34 ########################################################################
35 # NO __main__ entry allowed, use sat
36 ########################################################################
37 if __name__ == "__main__":
38     msg = """
39 ERROR: 'salomeTools.py' is not main command entry (CLI) for salomeTools.
40        Use 'sat' instead.\n\n"""
41     sys.stderr.write(msg)
42     sys.exit(_KOSYS)
43
44 # python imports
45 import os
46 import re
47 import tempfile
48 import imp
49 import types
50 import gettext
51 import traceback
52
53 import src
54 import src.debug as DBG # Easy print stderr (for DEBUG only)
55 import src.returnCode as RCO # Easy (ok/ko, why) return methods code
56 import src.utilsSat as UTS
57
58 # get path to salomeTools sources
59 satdir  = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
60 srcdir = os.path.join(satdir, 'src')
61 cmdsdir = os.path.join(satdir, 'commands')
62
63 import commands.config as CONFIG
64
65 # load resources for internationalization
66 gettext.install("salomeTools", os.path.join(srcdir, "i18n"))
67
68 _LANG = os.environ["LANG"] # original locale
69
70 # The possible hooks : 
71 # pre is for hooks to be executed before commands
72 # post is for hooks to be executed after commands
73 C_PRE_HOOK = "pre"
74 C_POST_HOOK = "post"
75
76 # Define all possible option for salomeTools command :  sat <option> <args>
77 parser = src.options.Options()
78 parser.add_option('h', 'help', 'boolean', 'help', 
79                   _("shows global help or help on a specific command."))
80 parser.add_option('o', 'overwrite', 'list', "overwrite", 
81                   _("overwrites a configuration parameters."))
82 parser.add_option('g', 'debug', 'boolean', 'debug_mode', 
83                   _("run salomeTools in debug mode."))
84 parser.add_option('v', 'verbose', 'int', "output_verbose_level", 
85                   _("change output verbose level (default is 3)."))
86 parser.add_option('b', 'batch', 'boolean', "batch", 
87                   _("batch mode (no question)."))
88 parser.add_option('t', 'all_in_terminal', 'boolean', "all_in_terminal", 
89                   _("All traces in the terminal (for example compilation logs)."))
90 parser.add_option('l', 'logs_paths_in_file', 'string', "logs_paths_in_file", 
91                   _("Put the command result and paths to log files in ."))
92
93
94 ########################################################################
95 # utility methods
96 ########################################################################
97 def find_command_list(dirPath):
98     ''' Parse files in dirPath that end with .py : it gives commands list
99     
100     :param dirPath str: The directory path where to search the commands
101     :return: cmd_list : the list containing the commands name 
102     :rtype: list
103     '''
104     cmd_list = []
105     for item in os.listdir(dirPath):
106         if "__init__" in item: continue # skip __init__.py
107         if item.endswith('.py'):
108             cmd_list.append(item[:-len('.py')])
109     return cmd_list
110
111
112 # The list of valid salomeTools commands from cmdsdir
113 # ['config', 'compile', 'prepare', ...]
114 _COMMANDS_NAMES = find_command_list(cmdsdir)
115 lCommand = find_command_list(cmdsdir) # obsolete
116
117 def getCommandsList():
118     """Gives commands list (as basename of files .py in directory commands""" 
119     return _COMMANDS_NAMES
120
121 def launchSat(command, logger=None):
122     """
123     launch sat as subprocess.Popen
124     command as string ('sat --help' for example)
125     used for unittest, or else...
126     
127     :return: RCO.ReturnCode with getValue as subprocess.Popen output
128     """
129     if "sat" not in command.split()[0]:
130       raise Exception(_("Not a valid command for launchSat: '%s'") % command)
131     env = dict(os.environ) # copy
132     # theorically useless, in user environ $PATH,
133     # on ne sait jamais
134     # https://docs.python.org/2/library/os.html
135     # On some platforms, including FreeBSD and Mac OS X, 
136     # setting environ may cause memory leaks.
137     # see test/initializeTest.py
138     if satdir not in env["PATH"].split(":"):
139       env["PATH"] = satdir + ":" + env["PATH"]
140     # TODO setLocale not 'fr' on subprocesses, why not?
141     # env["LANG"] == ''
142     res = UTS.Popen(command, env=env, logger=logger) # logger or not.
143     return res
144
145 def setNotLocale():
146     """force english at any moment"""
147     os.environ["LANG"] = ''
148     gettext.install("salomeTools", os.path.join(srcdir, "i18n"))
149     DBG.write("setNotLocale", os.environ["LANG"])
150     
151 def setLocale():
152     """
153     reset initial locale at any moment 
154     'fr' or else (TODO) from initial environment var '$LANG'
155     'i18n' as 'internationalization'
156     """
157     os.environ["LANG"] = _LANG
158     gettext.install("salomeTools", os.path.join(srcdir, "i18n"))
159     DBG.write("setLocale", os.environ["LANG"])
160     
161 def getVersion():
162     """get version number as string"""
163     return src.__version__
164  
165 def assumeAsList(strOrList):
166     """return a list as sys.argv if string"""
167     if type(strOrList) is list:
168       return list(strOrList) # copy
169     else:
170       res = strOrList.split(" ")
171       return [r for r in res if r != ""] # supposed string to split for convenience
172
173
174 ########################################################################
175 # Sat class
176 ########################################################################
177 class Sat(object):
178     """
179     The main class that stores all the commands of salomeTools
180     """
181     def __init__(self, logger=None):
182         """
183         Initialization
184
185         :param logger: The logger, if set from parent
186         """
187         # initialization of class attributes
188         self.__dict__ = dict()
189         # logger from parent
190         # future only one logger from src.loggingSimple at 2018/06
191         # to replace old loggers from src.logger
192         self.mainLogger = logger
193         src.logger.setCurrentLogger(logger)
194         self.cfg = None  # the config that will be read using pyconf module
195         self.arguments = None
196         self.remaindersArgs = None
197         self.options = None  # the options passed to salomeTools
198         self.datadir = None  # default value will be <salomeTools root>/data
199
200     def obsolete__init__(self, opt='', datadir=None):
201         '''Initialization
202
203         :param opt str: The sat options
204         :param: datadir str : the directory that contain all the external
205                               data (like software pyconf and software scripts)
206         '''
207         # Read the salomeTools options (the list of possible options is
208         # at the beginning of this file)
209         argList = self.assumeAsList(opt)
210         options, argus = parser.parse_args(argList)
211
212         # initialization of class attributes
213         self.__dict__ = dict()
214         self.cfg = None  # the config that will be read using pyconf module
215         self.arguments = argList
216         self.options = options  # the options passed to salomeTools
217         self.datadir = datadir  # default value will be <salomeTools root>/data
218         # set the commands by calling the dedicated function
219         self._setCommands(cmdsdir)
220
221         # if the help option has been called, print help and exit
222         if options.help:
223             try:
224                 self.print_help(argus)
225                 sys.exit(0)
226             except Exception as exc:
227                 write_exception(exc)
228                 sys.exit(1)
229
230     ##################################################################
231     def setInternals(self, opt=None, datadir=None):
232         """set the commands by calling the dedicated function etc..."""
233         options, remaindersArgs = parser.parse_args(opt)
234         self.arguments = opt
235         self.options = options # the generic options passed to salomeTools
236         self.remaindersArgs = remaindersArgs  # the command and their options
237         self.datadir = datadir # default value will be <salomeTools root>/data
238         self._setCommands(cmdsdir)
239
240     def getConfig(self):
241         return self.cfg
242
243     ##################################################################
244     def execute_cli(self, args):
245         """
246         assume launch command from args, pyconf config known yet
247         """
248         argList = self.assumeAsList(args)
249         # no arguments : print general help
250         if len(argList) == 0:
251           self.mainLogger.info(get_help())
252           return RCO.ReturnCode("OK", "no args as sat --help")
253
254         self.setInternals(opt=argList, datadir=None)
255
256         # print general help on -h
257         if self.options.help and len(self.remaindersArgs) == 0:
258           self.mainLogger.info(get_help())
259           return RCO.ReturnCode("OK", "help done")
260
261         DBG.write("options", self.options)
262         DBG.write("remaindersArgs", self.remaindersArgs)
263
264         if len(self.remaindersArgs) == 0:
265           return RCO.ReturnCode("KO", "Nothing to do")
266
267         # print command help on -h --help after name command
268         if "-h" in self.remaindersArgs or "--help" in self.remaindersArgs:
269           self.mainLogger.info(self.get_help(self.remaindersArgs))
270           return RCO.ReturnCode("OK", "sat --help command")
271
272         # print command help on -h and continue if something do do more
273         if self.options.help and len(self.remaindersArgs) >= 1:
274           self.mainLogger.info(self.get_help(self.remaindersArgs))
275
276         command = self.remaindersArgs[0]
277         # get dynamically the command function to call
278         fun_command = self.__getattr__(command)
279         # Run the command using the arguments
280         code = fun_command(self.remaindersArgs[1:])
281
282         if code is None: code = 0 # what?! do not know why so respect history
283
284         # return salomeTools command with the right message
285         # code (0 if no errors, else 1)
286         if code == _KOSYS:
287           return RCO.ReturnCode("KO", "problem on execute_cli 'sat %s'" % " ".join(argList))
288         else:
289           return RCO.ReturnCode("OK", "execute_cli 'sat %s' done" % " ".join(argList))
290
291     '''
292     # OBSOLETE... see file ../sat
293     # ###############################
294     # MAIN : terminal command usage #
295     # ###############################
296     if __name__ == "__main__":  
297         # Initialize the code that will be returned by the terminal command 
298         code = 0
299         (options, args) = parser.parse_args(sys.argv[1:])
300
301         # no arguments : print general help
302         if len(args) == 0:
303             print_help()
304             sys.exit(0)
305
306         # instantiate the salomeTools class with correct options
307         sat = Sat(sys.argv[1:])
308         # the command called
309         command = args[0]
310         # get dynamically the command function to call
311         fun_command = sat.__getattr__(command)
312         # Run the command using the arguments
313         code = fun_command(args[1:])
314
315         # exit salomeTools with the right code (0 if no errors, else 1)
316         if code is None: code = 0
317         sys.exit(code)
318
319     '''
320
321     def __getattr__(self, name):
322         '''
323         overwrite of __getattr__ function in order to display
324         a customized message in case of a wrong call
325         
326         :param name str: The name of the attribute 
327         '''
328         if name in self.__dict__:
329             return self.__dict__[name]
330         else:
331             raise AttributeError(name + _(" is not a valid command"))
332
333     def assumeAsList(self, strOrList):
334         # DBG.write("Sat assumeAsList", strOrList, True)
335         return assumeAsList(strOrList)
336     
337     def _setCommands(self, dirPath):
338         '''set class attributes corresponding to all commands that are 
339            in the dirPath directory
340         
341         :param dirPath str: The directory path containing the commands 
342         '''
343         # loop on the commands name
344         for nameCmd in lCommand:
345             DBG.write("load module command '%s.py'" % nameCmd, "")
346             # Exception for the jobs command that requires the paramiko module
347             if nameCmd == "jobs":
348                 try:
349                     saveout = sys.stderr
350                     ff = tempfile.TemporaryFile()
351                     sys.stderr = ff
352                     import paramiko
353                     sys.stderr = saveout
354                 except:
355                     sys.stderr = saveout
356                     continue
357
358             # load the module that has name nameCmd in dirPath
359             (file_, pathname, description) = imp.find_module(nameCmd, [dirPath])
360             module = imp.load_module(nameCmd, file_, pathname, description)
361             
362             def run_command(args='',
363                             options=None,
364                             batch = False,
365                             verbose = -1,
366                             logger_add_link = None):
367                 '''
368                 The function that will load the configuration (all pyconf)
369                 and return the function run of the command corresponding to module
370                 
371                 :param args str: The arguments of the command 
372                 '''
373                 # Make sure the internationalization is available
374                 gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
375                 
376                 # Get the arguments in a list and remove the empty elements
377                 if type(args) == type(''):
378                     # split by spaces without considering spaces in quotes
379                     argv_0 = re.findall(r'(?:"[^"]*"|[^\s"])+', args)
380                 else:
381                     argv_0 = args
382                 
383                 if argv_0 != ['']:
384                     while "" in argv_0: argv_0.remove("")
385                 
386                 # Format the argv list in order to prevent strings 
387                 # that contain a blank to be separated
388                 argv = []
389                 elem_old = ""
390                 for elem in argv_0:
391                     if argv == [] or elem_old.startswith("-") or elem.startswith("-"):
392                         argv.append(elem)
393                     else:
394                         argv[-1] += " " + elem
395                     elem_old = elem
396                            
397                 # if it is provided by the command line, get the application
398                 appliToLoad = None
399                 if argv not in [[''], []] and argv[0][0] != "-":
400                     appliToLoad = argv[0].rstrip('*')
401                     argv = argv[1:]
402                 
403                 # Check if the global options of salomeTools have to be changed
404                 if options:
405                     options_save = self.options
406                     self.options = options  
407
408                 # read the configuration from all the pyconf files    
409                 cfgManager = CONFIG.ConfigManager()
410                 self.cfg = cfgManager.get_config(datadir=self.datadir, 
411                                                  application=appliToLoad, 
412                                                  options=self.options, 
413                                                  command=__nameCmd__)
414                                
415                 # Set the verbose mode if called
416                 if verbose > -1:
417                     verbose_save = self.options.output_verbose_level
418                     self.options.__setattr__("output_verbose_level", verbose)    
419
420                 # Set batch mode if called
421                 if batch:
422                     batch_save = self.options.batch
423                     self.options.__setattr__("batch", True)
424
425                 # set output level
426                 if self.options.output_verbose_level is not None:
427                     self.cfg.USER.output_verbose_level = self.options.output_verbose_level
428                 if self.cfg.USER.output_verbose_level < 1:
429                     self.cfg.USER.output_verbose_level = 0
430                 silent = (self.cfg.USER.output_verbose_level == 0)
431
432                 # create log file
433                 micro_command = False
434                 if logger_add_link:
435                     micro_command = True
436                 logger_command = src.logger.Logger(self.cfg,
437                                    silent_sysstd=silent,
438                                    all_in_terminal=self.options.all_in_terminal,
439                                    micro_command=micro_command)
440                 
441                 # Check that the path given by the logs_paths_in_file option
442                 # is a file path that can be written
443                 if self.options.logs_paths_in_file and not micro_command:
444                     try:
445                         self.options.logs_paths_in_file = os.path.abspath(
446                                                 self.options.logs_paths_in_file)
447                         dir_file = os.path.dirname(self.options.logs_paths_in_file)
448                         if not os.path.exists(dir_file):
449                             os.makedirs(dir_file)
450                         if os.path.exists(self.options.logs_paths_in_file):
451                             os.remove(self.options.logs_paths_in_file)
452                         file_test = open(self.options.logs_paths_in_file, "w")
453                         file_test.close()
454                     except Exception as e:
455                         msg = _("WARNING: the logs_paths_in_file option will "
456                                 "not be taken into account.\nHere is the error:")
457                         logger_command.write("%s\n%s\n\n" % (
458                                              src.printcolors.printcWarning(msg),
459                                              str(e)))
460                         self.options.logs_paths_in_file = None
461                 
462                 options_launched = ""
463                 res = None
464                 try:
465                     # Execute the hooks (if there is any) 
466                     # and run method of the command
467                     self.run_hook(__nameCmd__, C_PRE_HOOK, logger_command)
468                     res = __module__.run(argv, self, logger_command)
469                     self.run_hook(__nameCmd__, C_POST_HOOK, logger_command)
470                     if res is None:
471                         res = 0
472                         
473                 except Exception as e:
474                     # Get error
475                     logger_command.write("\n***** ", 1)
476                     logger_command.write(src.printcolors.printcError(
477                                                        "salomeTools ERROR:"), 1)
478                     logger_command.write("\n" + str(e) + "\n\n", 1)
479                     # get stack
480                     __, __, exc_traceback = sys.exc_info()
481                     fp = tempfile.TemporaryFile()
482                     traceback.print_tb(exc_traceback, file=fp)
483                     fp.seek(0)
484                     stack = fp.read()
485                     verbosity = 5
486                     if self.options.debug_mode:
487                         verbosity = 1
488                     logger_command.write("TRACEBACK: %s" % stack.replace('"',"'"),
489                                          verbosity)
490                 finally:
491                     # set res if it is not set in the command
492                     if res is None:
493                         res = 1
494                                             
495                     # come back to the original global options
496                     if options:
497                         options_launched = get_text_from_options(self.options)
498                         self.options = options_save
499                     
500                     # come back in the original batch mode if 
501                     # batch argument was called
502                     if batch:
503                         self.options.__setattr__("batch", batch_save)
504
505                     # come back in the original verbose mode if 
506                     # verbose argument was called                        
507                     if verbose > -1:
508                         self.options.__setattr__("output_verbose_level", 
509                                                  verbose_save)
510                     # put final attributes in xml log file 
511                     # (end time, total time, ...) and write it
512                     launchedCommand = ' '.join([self.cfg.VARS.salometoolsway +
513                                                 os.path.sep +
514                                                 'sat',
515                                                 options_launched,
516                                                 __nameCmd__, 
517                                                 ' '.join(argv_0)])
518                     # TODO may be no need as call escapeSequence xml
519                     launchedCommand = launchedCommand.replace('"', "'")
520                     
521                     # Add a link to the parent command      
522                     if logger_add_link is not None:
523                         logger_add_link.add_link(logger_command.logFileName,
524                                                  __nameCmd__,
525                                                  res,
526                                                  launchedCommand)
527                         logger_add_link.l_logFiles += logger_command.l_logFiles
528                                             
529                     # Put the final attributes corresponding to end time and
530                     # Write the file to the hard drive
531                     logger_command.end_write(
532                                         {"launchedCommand" : launchedCommand})
533                     
534                     if res != 0:
535                         res = 1
536                         
537                     # print the log file path if 
538                     # the maximum verbose mode is invoked
539                     if not micro_command:
540                         logger_command.write("\nPath to the xml log file :\n",
541                                              5)
542                         logger_command.write("%s\n\n" % src.printcolors.printcInfo(
543                                                 logger_command.logFilePath), 5)
544
545                     # If the logs_paths_in_file was called, write the result
546                     # and log files in the given file path
547                     if self.options.logs_paths_in_file and not micro_command:
548                         file_res = open(self.options.logs_paths_in_file, "w")
549                         file_res.write(str(res) + "\n")
550                         for i, filepath in enumerate(logger_command.l_logFiles):
551                             file_res.write(filepath)
552                             if i < len(logger_command.l_logFiles):
553                                 file_res.write("\n")
554                                 file_res.flush()
555                 
556                 return res
557
558             # Make sure that run_command will be redefined 
559             # at each iteration of the loop
560             globals_up = {}
561             globals_up.update(run_command.__globals__)
562             globals_up.update({'__nameCmd__': nameCmd, '__module__' : module})
563             func = types.FunctionType(run_command.__code__,
564                                       globals_up,
565                                       run_command.__name__,
566                                       run_command.__defaults__,
567                                       run_command.__closure__)
568
569             # set the attribute corresponding to the command
570             self.__setattr__(nameCmd, func)
571
572     def run_hook(self, cmd_name, hook_type, logger):
573         '''Execute a hook file for a given command regarding the fact 
574            it is pre or post
575         
576         :param cmd_name str: The the command on which execute the hook
577         :param hook_type str: pre or post
578         :param logger Logger: the logging instance to use for the prints
579         '''
580         # The hooks must be defined in the application pyconf
581         # So, if there is no application, do not do anything
582         if not src.config_has_application(self.cfg):
583             return
584
585         # The hooks must be defined in the application pyconf in the
586         # APPLICATION section, hook : { command : 'script_path.py'}
587         if "hook" not in self.cfg.APPLICATION \
588                     or cmd_name not in self.cfg.APPLICATION.hook:
589             return
590
591         # Get the hook_script path and verify that it exists
592         hook_script_path = self.cfg.APPLICATION.hook[cmd_name]
593         if not os.path.exists(hook_script_path):
594             raise src.SatException(_("Hook script not found: %s") % 
595                                    hook_script_path)
596         
597         # Try to execute the script, catch the exception if it fails
598         try:
599             # import the module (in the sense of python)
600             pymodule = imp.load_source(cmd_name, hook_script_path)
601             
602             # format a message to be printed at hook execution
603             msg = src.printcolors.printcWarning(_("Run hook script"))
604             msg = "%s: %s\n" % (msg, 
605                                 src.printcolors.printcInfo(hook_script_path))
606             
607             # run the function run_pre_hook if this function is called 
608             # before the command, run_post_hook if it is called after
609             if hook_type == C_PRE_HOOK and "run_pre_hook" in dir(pymodule):
610                 logger.write(msg, 1)
611                 pymodule.run_pre_hook(self.cfg, logger)
612             elif hook_type == C_POST_HOOK and "run_post_hook" in dir(pymodule):
613                 logger.write(msg, 1)
614                 pymodule.run_post_hook(self.cfg, logger)
615
616         except Exception as exc:
617             msg = _("Unable to run hook script: %s") % hook_script_path
618             msg += "\n" + str(exc)
619             raise src.SatException(msg)
620
621     def get_help(self, opt):
622         '''Prints help for a command. Function called when "sat -h <command>"
623         
624         :param argv str: the options passed (to get the command name)
625         '''
626         # if no command as argument (sat -h)
627         if len(opt)==0:
628             return get_help()
629         # get command name
630         command = opt[0]
631         # read the configuration from all the pyconf files
632         cfgManager = CONFIG.ConfigManager()
633         self.cfg = cfgManager.get_config(datadir=self.datadir)
634
635         # Check if this command exists
636         if not hasattr(self, command):
637             raise src.SatException(_("Command '%s' does not exist") % command)
638         
639         # Print salomeTools version
640         msg = "\n" + get_version() + "\n\n"
641         
642         # load the module
643         module = self.get_module(command)
644
645         # print the description of the command that is done in the command file
646         if hasattr( module, "description" ) :
647             msg += src.printcolors.printcHeader( _("Description:") )
648             msg += '\n' + module.description() + '\n\n'
649
650         # print the description of the command options
651         if hasattr( module, "parser" ):
652             msg += module.parser.get_help()
653
654         msg += "\n -h, --help (boolean)\n          shows help on command.\n"
655         return msg
656
657     def get_module(self, module):
658         '''Loads a command. Function called only by print_help
659         
660         :param module str: the command to load
661         '''
662         # Check if this command exists
663         if not hasattr(self, module):
664             raise src.SatException(_("Command '%s' does not exist") % module)
665
666         # load the module
667         (file_, pathname, description) = imp.find_module(module, [cmdsdir])
668         module = imp.load_module(module, file_, pathname, description)
669         return module
670
671 ##################################################################
672 def get_text_from_options(options):
673     text_options = ""
674     for attr in dir(options):
675         if attr.startswith("__"):
676             continue
677         if options.__getattr__(attr) != None:
678             option_contain = options.__getattr__(attr)
679             if type(option_contain)==type([]):
680                 option_contain = ",".join(option_contain)
681             if type(option_contain)==type(True):
682                 option_contain = ""
683             text_options+= "--%s %s " % (attr, option_contain)
684     return text_options
685                 
686
687 def get_version():
688     '''
689     get colorized salomeTools version (in src/internal_config/salomeTools.pyconf).
690     returns string
691     '''
692     # read the config 
693     cfgManager = CONFIG.ConfigManager()
694     cfg = cfgManager.get_config()
695     # print the key corresponding to salomeTools version
696     msg = (src.printcolors.printcHeader( _("Version: ") ) + cfg.INTERNAL.sat_version)
697     return msg
698
699
700 def get_help():
701     '''
702     get salomeTools general help.
703     returns string
704     '''
705     msg = "\n" + get_version() + "\n\n"
706     msg += src.printcolors.printcHeader( _("Usage: ") ) + \
707           "sat [sat_options] <command> [product] [command_options]\n\n"
708
709     msg += parser.get_help() + "\n"
710
711     # display all the available commands.
712     msg += src.printcolors.printcHeader(_("Available commands are:")) + "\n"
713     for command in lCommand:
714         msg += " - %s\n" % (command)
715
716     msg += "\n"
717     # Explain how to get the help for a specific command
718     msg += src.printcolors.printcHeader(
719         _("Getting the help for a specific command: ")) + \
720         "\n>> sat --help <command>\n"
721     return msg
722
723 def write_exception(exc):
724     '''write exception in case of error in a command
725     
726     :param exc exception: the exception to print
727     '''
728     sys.stderr.write("\n***** ")
729     sys.stderr.write(src.printcolors.printcError("salomeTools ERROR:"))
730     sys.stderr.write("\n" + str(exc) + "\n")
731
732