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