]> SALOME platform Git repositories - tools/sat.git/blob - src/salomeTools.py
Salome HOME
add AllTestLauncher and src/loggingSimple.py etc...
[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                     launchedCommand = launchedCommand.replace('"', "'")
519                     
520                     # Add a link to the parent command      
521                     if logger_add_link is not None:
522                         logger_add_link.add_link(logger_command.logFileName,
523                                                  __nameCmd__,
524                                                  res,
525                                                  launchedCommand)
526                         logger_add_link.l_logFiles += logger_command.l_logFiles
527                                             
528                     # Put the final attributes corresponding to end time and
529                     # Write the file to the hard drive
530                     logger_command.end_write(
531                                         {"launchedCommand" : launchedCommand})
532                     
533                     if res != 0:
534                         res = 1
535                         
536                     # print the log file path if 
537                     # the maximum verbose mode is invoked
538                     if not micro_command:
539                         logger_command.write("\nPath to the xml log file :\n",
540                                              5)
541                         logger_command.write("%s\n\n" % src.printcolors.printcInfo(
542                                                 logger_command.logFilePath), 5)
543
544                     # If the logs_paths_in_file was called, write the result
545                     # and log files in the given file path
546                     if self.options.logs_paths_in_file and not micro_command:
547                         file_res = open(self.options.logs_paths_in_file, "w")
548                         file_res.write(str(res) + "\n")
549                         for i, filepath in enumerate(logger_command.l_logFiles):
550                             file_res.write(filepath)
551                             if i < len(logger_command.l_logFiles):
552                                 file_res.write("\n")
553                                 file_res.flush()
554                 
555                 return res
556
557             # Make sure that run_command will be redefined 
558             # at each iteration of the loop
559             globals_up = {}
560             globals_up.update(run_command.__globals__)
561             globals_up.update({'__nameCmd__': nameCmd, '__module__' : module})
562             func = types.FunctionType(run_command.__code__,
563                                       globals_up,
564                                       run_command.__name__,
565                                       run_command.__defaults__,
566                                       run_command.__closure__)
567
568             # set the attribute corresponding to the command
569             self.__setattr__(nameCmd, func)
570
571     def run_hook(self, cmd_name, hook_type, logger):
572         '''Execute a hook file for a given command regarding the fact 
573            it is pre or post
574         
575         :param cmd_name str: The the command on which execute the hook
576         :param hook_type str: pre or post
577         :param logger Logger: the logging instance to use for the prints
578         '''
579         # The hooks must be defined in the application pyconf
580         # So, if there is no application, do not do anything
581         if not src.config_has_application(self.cfg):
582             return
583
584         # The hooks must be defined in the application pyconf in the
585         # APPLICATION section, hook : { command : 'script_path.py'}
586         if "hook" not in self.cfg.APPLICATION \
587                     or cmd_name not in self.cfg.APPLICATION.hook:
588             return
589
590         # Get the hook_script path and verify that it exists
591         hook_script_path = self.cfg.APPLICATION.hook[cmd_name]
592         if not os.path.exists(hook_script_path):
593             raise src.SatException(_("Hook script not found: %s") % 
594                                    hook_script_path)
595         
596         # Try to execute the script, catch the exception if it fails
597         try:
598             # import the module (in the sense of python)
599             pymodule = imp.load_source(cmd_name, hook_script_path)
600             
601             # format a message to be printed at hook execution
602             msg = src.printcolors.printcWarning(_("Run hook script"))
603             msg = "%s: %s\n" % (msg, 
604                                 src.printcolors.printcInfo(hook_script_path))
605             
606             # run the function run_pre_hook if this function is called 
607             # before the command, run_post_hook if it is called after
608             if hook_type == C_PRE_HOOK and "run_pre_hook" in dir(pymodule):
609                 logger.write(msg, 1)
610                 pymodule.run_pre_hook(self.cfg, logger)
611             elif hook_type == C_POST_HOOK and "run_post_hook" in dir(pymodule):
612                 logger.write(msg, 1)
613                 pymodule.run_post_hook(self.cfg, logger)
614
615         except Exception as exc:
616             msg = _("Unable to run hook script: %s") % hook_script_path
617             msg += "\n" + str(exc)
618             raise src.SatException(msg)
619
620     def get_help(self, opt):
621         '''Prints help for a command. Function called when "sat -h <command>"
622         
623         :param argv str: the options passed (to get the command name)
624         '''
625         # if no command as argument (sat -h)
626         if len(opt)==0:
627             return get_help()
628         # get command name
629         command = opt[0]
630         # read the configuration from all the pyconf files
631         cfgManager = CONFIG.ConfigManager()
632         self.cfg = cfgManager.get_config(datadir=self.datadir)
633
634         # Check if this command exists
635         if not hasattr(self, command):
636             raise src.SatException(_("Command '%s' does not exist") % command)
637         
638         # Print salomeTools version
639         msg = "\n" + get_version() + "\n\n"
640         
641         # load the module
642         module = self.get_module(command)
643
644         # print the description of the command that is done in the command file
645         if hasattr( module, "description" ) :
646             msg += src.printcolors.printcHeader( _("Description:") )
647             msg += '\n' + module.description() + '\n\n'
648
649         # print the description of the command options
650         if hasattr( module, "parser" ):
651             msg += module.parser.get_help()
652
653         msg += "\n -h, --help (boolean)\n          shows help on command.\n"
654         return msg
655
656     def get_module(self, module):
657         '''Loads a command. Function called only by print_help
658         
659         :param module str: the command to load
660         '''
661         # Check if this command exists
662         if not hasattr(self, module):
663             raise src.SatException(_("Command '%s' does not exist") % module)
664
665         # load the module
666         (file_, pathname, description) = imp.find_module(module, [cmdsdir])
667         module = imp.load_module(module, file_, pathname, description)
668         return module
669
670 ##################################################################
671 def get_text_from_options(options):
672     text_options = ""
673     for attr in dir(options):
674         if attr.startswith("__"):
675             continue
676         if options.__getattr__(attr) != None:
677             option_contain = options.__getattr__(attr)
678             if type(option_contain)==type([]):
679                 option_contain = ",".join(option_contain)
680             if type(option_contain)==type(True):
681                 option_contain = ""
682             text_options+= "--%s %s " % (attr, option_contain)
683     return text_options
684                 
685
686 def get_version():
687     '''
688     get colorized salomeTools version (in src/internal_config/salomeTools.pyconf).
689     returns string
690     '''
691     # read the config 
692     cfgManager = CONFIG.ConfigManager()
693     cfg = cfgManager.get_config()
694     # print the key corresponding to salomeTools version
695     msg = (src.printcolors.printcHeader( _("Version: ") ) + cfg.INTERNAL.sat_version)
696     return msg
697
698
699 def get_help():
700     '''
701     get salomeTools general help.
702     returns string
703     '''
704     msg = "\n" + get_version() + "\n\n"
705     msg += src.printcolors.printcHeader( _("Usage: ") ) + \
706           "sat [sat_options] <command> [product] [command_options]\n\n"
707
708     msg += parser.get_help() + "\n"
709
710     # display all the available commands.
711     msg += src.printcolors.printcHeader(_("Available commands are:")) + "\n"
712     for command in lCommand:
713         msg += " - %s\n" % (command)
714
715     msg += "\n"
716     # Explain how to get the help for a specific command
717     msg += src.printcolors.printcHeader(
718         _("Getting the help for a specific command: ")) + \
719         "\n>> sat --help <command>\n"
720     return msg
721
722 def write_exception(exc):
723     '''write exception in case of error in a command
724     
725     :param exc exception: the exception to print
726     '''
727     sys.stderr.write("\n***** ")
728     sys.stderr.write(src.printcolors.printcError("salomeTools ERROR:"))
729     sys.stderr.write("\n" + str(exc) + "\n")
730
731