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