Salome HOME
sat #20679 : maj doc pdf
[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         if options.debug_mode:
238             DBG.push_debug(True)
239         self.arguments = opt
240         self.options = options # the generic options passed to salomeTools
241         self.remaindersArgs = remaindersArgs  # the command and their options
242         self.datadir = datadir # default value will be <salomeTools root>/data
243         self._setCommands(cmdsdir)
244         DBG.write("Sat.options", self.options, self.options.debug_mode)
245
246     def getConfig(self):
247         return self.cfg
248
249     ##################################################################
250     def execute_cli(self, args):
251         """
252         assume launch command from args, pyconf config known yet
253         """
254         argList = self.assumeAsList(args)
255         # no arguments : print general help
256         if len(argList) == 0:
257           self.mainLogger.info(get_help())
258           return RCO.ReturnCode("OK", "no args as sat --help")
259
260         self.setInternals(opt=argList, datadir=None)
261
262         # print general help on -h
263         if self.options.help and len(self.remaindersArgs) == 0:
264           self.mainLogger.info(get_help())
265           return RCO.ReturnCode("OK", "help done")
266
267         DBG.write("options", self.options)
268         DBG.write("remaindersArgs", self.remaindersArgs)
269
270         if len(self.remaindersArgs) == 0:
271           return RCO.ReturnCode("KO", "Nothing to do")
272
273         # print command help on -h --help after name command
274         if "-h" in self.remaindersArgs or "--help" in self.remaindersArgs:
275           self.mainLogger.info(self.get_help(self.remaindersArgs))
276           return RCO.ReturnCode("OK", "sat --help command")
277
278         # print command help on -h and continue if something do do more
279         if self.options.help and len(self.remaindersArgs) >= 1:
280           self.mainLogger.info(self.get_help(self.remaindersArgs))
281
282         command = self.remaindersArgs[0]
283         # get dynamically the command function to call
284         fun_command = self.__getattr__(command)
285         # Run the command using the arguments
286         code = fun_command(self.remaindersArgs[1:])
287
288         if code is None: code = 0 # what?! do not know why so respect history
289
290         # return salomeTools command with the right message
291         # code (0 if no errors, else 1)
292         if code == _KOSYS:
293           return RCO.ReturnCode("KO", "problem on execute_cli 'sat %s'" % " ".join(argList))
294         else:
295           return RCO.ReturnCode("OK", "execute_cli 'sat %s' done" % " ".join(argList))
296
297     '''
298     # OBSOLETE... see file ../sat
299     # ###############################
300     # MAIN : terminal command usage #
301     # ###############################
302     if __name__ == "__main__":  
303         # Initialize the code that will be returned by the terminal command 
304         code = 0
305         (options, args) = parser.parse_args(sys.argv[1:])
306
307         # no arguments : print general help
308         if len(args) == 0:
309             print_help()
310             sys.exit(0)
311
312         # instantiate the salomeTools class with correct options
313         sat = Sat(sys.argv[1:])
314         # the command called
315         command = args[0]
316         # get dynamically the command function to call
317         fun_command = sat.__getattr__(command)
318         # Run the command using the arguments
319         code = fun_command(args[1:])
320
321         # exit salomeTools with the right code (0 if no errors, else 1)
322         if code is None: code = 0
323         sys.exit(code)
324
325     '''
326
327     def __getattr__(self, name):
328         '''
329         overwrite of __getattr__ function in order to display
330         a customized message in case of a wrong call
331         
332         :param name str: The name of the attribute 
333         '''
334         if name in self.__dict__:
335             return self.__dict__[name]
336         else:
337             raise AttributeError(name + _(" is not a valid command"))
338
339     def assumeAsList(self, strOrList):
340         # DBG.write("Sat assumeAsList", strOrList, True)
341         return assumeAsList(strOrList)
342     
343     def _setCommands(self, dirPath):
344         '''set class attributes corresponding to all commands that are 
345            in the dirPath directory
346         
347         :param dirPath str: The directory path containing the commands 
348         '''
349         # loop on the commands name
350         for nameCmd in lCommand:
351             # Exception for the jobs command that requires the paramiko module
352             if nameCmd == "jobs":
353                 try:
354                     saveout = sys.stderr
355                     ff = tempfile.TemporaryFile()
356                     sys.stderr = ff
357                     import paramiko
358                     sys.stderr = saveout
359                 except:
360                     sys.stderr = saveout
361                     continue
362
363             # load the module that has name nameCmd in dirPath
364             (file_, pathname, description) = imp.find_module(nameCmd, [dirPath])
365             module = imp.load_module(nameCmd, file_, pathname, description)
366             
367             def run_command(args='',
368                             options=None,
369                             batch = False,
370                             verbose = -1,
371                             logger_add_link = None):
372                 '''
373                 The function that will load the configuration (all pyconf)
374                 and return the function run of the command corresponding to module
375                 
376                 :param args str: The arguments of the command 
377                 '''
378                 # Make sure the internationalization is available
379                 gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
380                 
381                 # Get the arguments in a list and remove the empty elements
382                 if type(args) == type(''):
383                     # split by spaces without considering spaces in quotes
384                     argv_0 = re.findall(r'(?:"[^"]*"|[^\s"])+', args)
385                 else:
386                     argv_0 = args
387                 
388                 if argv_0 != ['']:
389                     while "" in argv_0: argv_0.remove("")
390                 
391                 # Format the argv list in order to prevent strings 
392                 # that contain a blank to be separated
393                 argv = []
394                 elem_old = ""
395                 for elem in argv_0:
396                     if argv == [] or elem_old.startswith("-") or elem.startswith("-"):
397                         argv.append(elem)
398                     else:
399                         argv[-1] += " " + elem
400                     elem_old = elem
401                            
402                 # if it is provided by the command line, get the application
403                 appliToLoad = None
404                 if argv not in [[''], []] and argv[0][0] != "-":
405                     appliToLoad = argv[0].rstrip('*')
406                     argv = argv[1:]
407                 
408                 # Check if the global options of salomeTools have to be changed
409                 if options:
410                     options_save = self.options
411                     self.options = options  
412
413                 # read the configuration from all the pyconf files    
414                 cfgManager = CONFIG.ConfigManager()
415                 self.cfg = cfgManager.get_config(datadir=self.datadir, 
416                                                  application=appliToLoad, 
417                                                  options=self.options, 
418                                                  command=__nameCmd__)
419                                
420                 # Set the verbose mode if called
421                 if verbose > -1:
422                     verbose_save = self.options.output_verbose_level
423                     self.options.__setattr__("output_verbose_level", verbose)    
424
425                 # Set batch mode if called
426                 if batch:
427                     batch_save = self.options.batch
428                     self.options.__setattr__("batch", True)
429
430                 # set output level
431                 if self.options.output_verbose_level is not None:
432                     self.cfg.USER.output_verbose_level = self.options.output_verbose_level
433                 if self.cfg.USER.output_verbose_level < 1:
434                     self.cfg.USER.output_verbose_level = 0
435                 silent = (self.cfg.USER.output_verbose_level == 0)
436
437                 # create log file
438                 micro_command = False
439                 if logger_add_link:
440                     micro_command = True
441                 logger_command = src.logger.Logger(self.cfg,
442                                    silent_sysstd=silent,
443                                    all_in_terminal=self.options.all_in_terminal,
444                                    micro_command=micro_command)
445                 
446                 # Check that the path given by the logs_paths_in_file option
447                 # is a file path that can be written
448                 if self.options.logs_paths_in_file and not micro_command:
449                     try:
450                         self.options.logs_paths_in_file = os.path.abspath(
451                                                 self.options.logs_paths_in_file)
452                         dir_file = os.path.dirname(self.options.logs_paths_in_file)
453                         if not os.path.exists(dir_file):
454                             os.makedirs(dir_file)
455                         if os.path.exists(self.options.logs_paths_in_file):
456                             os.remove(self.options.logs_paths_in_file)
457                         file_test = open(self.options.logs_paths_in_file, "w")
458                         file_test.close()
459                     except Exception as e:
460                         msg = _("WARNING: the logs_paths_in_file option will "
461                                 "not be taken into account.\nHere is the error:")
462                         logger_command.write("%s\n%s\n\n" % (
463                                              src.printcolors.printcWarning(msg),
464                                              str(e)))
465                         self.options.logs_paths_in_file = None
466
467
468                 # do nothing more if help is True
469                 if self.options.help:
470                   return 0
471
472                 options_launched = ""
473                 res = None
474                 try:
475                     # Execute the hooks (if there is any) 
476                     # and run method of the command
477                     self.run_hook(__nameCmd__, C_PRE_HOOK, logger_command)
478                     res = __module__.run(argv, self, logger_command)
479                     self.run_hook(__nameCmd__, C_POST_HOOK, logger_command)
480                     if res is None:
481                         res = 0
482                         
483                 except src.SatException as e:
484                     # for sat exception do not display the stack, unless debug mode is set
485                     logger_command.write("\n***** ", 1)
486                     logger_command.write(src.printcolors.printcError(
487                             "salomeTools ERROR: sat %s" % __nameCmd__), 1)
488                     logger_command.write(" *****\n", 1)
489                     print(e.message)
490                     if self.options.debug_mode:
491                         logger_command.write("\n" + DBG.format_exception("") + "\n", 1)
492
493                 except Exception as e:
494                     # here we print the stack in addition
495                     logger_command.write("\n***** ", 1)
496                     logger_command.write(src.printcolors.printcError(
497                             "salomeTools ERROR: sat %s" % __nameCmd__), 1)
498
499                     logger_command.write("\n" + DBG.format_exception("") + "\n", 1)
500
501
502                 finally:
503                     # set res if it is not set in the command
504                     if res is None:
505                         res = 1
506                                             
507                     # come back to the original global options
508                     if options:
509                         options_launched = get_text_from_options(self.options)
510                         self.options = options_save
511                     
512                     # come back in the original batch mode if 
513                     # batch argument was called
514                     if batch:
515                         self.options.__setattr__("batch", batch_save)
516
517                     # come back in the original verbose mode if 
518                     # verbose argument was called                        
519                     if verbose > -1:
520                         self.options.__setattr__("output_verbose_level", 
521                                                  verbose_save)
522                     # put final attributes in xml log file 
523                     # (end time, total time, ...) and write it
524                     launchedCommand = ' '.join([self.cfg.VARS.salometoolsway +
525                                                 os.path.sep +
526                                                 'sat',
527                                                 options_launched,
528                                                 __nameCmd__, 
529                                                 ' '.join(argv_0)])
530                     # TODO may be no need as call escapeSequence xml
531                     launchedCommand = launchedCommand.replace('"', "'")
532                     
533                     # Add a link to the parent command      
534                     if logger_add_link is not None:
535                         logger_add_link.add_link(logger_command.logFileName,
536                                                  __nameCmd__,
537                                                  res,
538                                                  launchedCommand)
539                         logger_add_link.l_logFiles += logger_command.l_logFiles
540                                             
541                     # Put the final attributes corresponding to end time and
542                     # Write the file to the hard drive
543                     logger_command.end_write(
544                                         {"launchedCommand" : launchedCommand})
545                     
546                     if res != 0:
547                         res = 1
548                         
549                     # print the log file path if 
550                     # the maximum verbose mode is invoked
551                     if not micro_command:
552                         logger_command.write("\nPath to the xml log file :\n",
553                                              5)
554                         logger_command.write("%s\n\n" % src.printcolors.printcInfo(
555                                                 logger_command.logFilePath), 5)
556
557                     # If the logs_paths_in_file was called, write the result
558                     # and log files in the given file path
559                     if self.options.logs_paths_in_file and not micro_command:
560                         file_res = open(self.options.logs_paths_in_file, "w")
561                         file_res.write(str(res) + "\n")
562                         for i, filepath in enumerate(logger_command.l_logFiles):
563                             file_res.write(filepath)
564                             if i < len(logger_command.l_logFiles):
565                                 file_res.write("\n")
566                                 file_res.flush()
567                 
568                 return res
569
570             # Make sure that run_command will be redefined 
571             # at each iteration of the loop
572             globals_up = {}
573             globals_up.update(run_command.__globals__)
574             globals_up.update({'__nameCmd__': nameCmd, '__module__' : module})
575             func = types.FunctionType(run_command.__code__,
576                                       globals_up,
577                                       run_command.__name__,
578                                       run_command.__defaults__,
579                                       run_command.__closure__)
580
581             # set the attribute corresponding to the command
582             self.__setattr__(nameCmd, func)
583
584     def run_hook(self, cmd_name, hook_type, logger):
585         '''Execute a hook file for a given command regarding the fact 
586            it is pre or post
587         
588         :param cmd_name str: The the command on which execute the hook
589         :param hook_type str: pre or post
590         :param logger Logger: the logging instance to use for the prints
591         '''
592         # The hooks must be defined in the application pyconf
593         # So, if there is no application, do not do anything
594         if not src.config_has_application(self.cfg):
595             return
596
597         # The hooks must be defined in the application pyconf in the
598         # APPLICATION section, hook : { command : 'script_path.py'}
599         if "hook" not in self.cfg.APPLICATION \
600                     or cmd_name not in self.cfg.APPLICATION.hook:
601             return
602
603         # Get the hook_script path and verify that it exists
604         hook_script_path = self.cfg.APPLICATION.hook[cmd_name]
605         if not os.path.exists(hook_script_path):
606             raise src.SatException(_("Hook script not found: %s") % 
607                                    hook_script_path)
608         
609         # Try to execute the script, catch the exception if it fails
610         try:
611             # import the module (in the sense of python)
612             pymodule = imp.load_source(cmd_name, hook_script_path)
613             
614             # format a message to be printed at hook execution
615             msg = src.printcolors.printcWarning(_("Run hook script"))
616             msg = "%s: %s\n" % (msg, 
617                                 src.printcolors.printcInfo(hook_script_path))
618             
619             # run the function run_pre_hook if this function is called 
620             # before the command, run_post_hook if it is called after
621             if hook_type == C_PRE_HOOK and "run_pre_hook" in dir(pymodule):
622                 logger.write(msg, 1)
623                 pymodule.run_pre_hook(self.cfg, logger)
624             elif hook_type == C_POST_HOOK and "run_post_hook" in dir(pymodule):
625                 logger.write(msg, 1)
626                 pymodule.run_post_hook(self.cfg, logger)
627
628         except Exception as exc:
629             msg = _("Unable to run hook script: %s") % hook_script_path
630             msg += "\n" + str(exc)
631             raise src.SatException(msg)
632
633     def get_help(self, opt):
634         '''Prints help for a command. Function called when "sat -h <command>"
635         
636         :param argv str: the options passed (to get the command name)
637         '''
638         # if no command as argument (sat -h)
639         if len(opt)==0:
640             return get_help()
641         # get command name
642         command = opt[0]
643         # read the configuration from all the pyconf files
644         cfgManager = CONFIG.ConfigManager()
645         self.cfg = cfgManager.get_config(datadir=self.datadir)
646
647         # Check if this command exists
648         if not hasattr(self, command):
649             raise src.SatException(_("Command '%s' does not exist") % command)
650         
651         # Print salomeTools version
652         msg = "\n" + get_version() + "\n\n"
653         
654         # load the module
655         module = self.get_module(command)
656
657         # print the description of the command that is done in the command file
658         if hasattr( module, "description" ) :
659             msg += src.printcolors.printcHeader( _("Description:") )
660             msg += '\n' + module.description() + '\n\n'
661
662         # print the description of the command options
663         if hasattr( module, "parser" ):
664             msg += module.parser.get_help()
665
666         msg += "\n -h, --help (boolean)\n          shows help on command.\n"
667         return msg
668
669     def get_module(self, module):
670         '''Loads a command. Function called only by print_help
671         
672         :param module str: the command to load
673         '''
674         # Check if this command exists
675         if not hasattr(self, module):
676             raise src.SatException(_("Command '%s' does not exist") % module)
677
678         # load the module
679         (file_, pathname, description) = imp.find_module(module, [cmdsdir])
680         module = imp.load_module(module, file_, pathname, description)
681         return module
682
683 ##################################################################
684 def get_text_from_options(options):
685     text_options = ""
686     for attr in dir(options):
687         if attr.startswith("__"):
688             continue
689         if options.__getattr__(attr) != None:
690             option_contain = options.__getattr__(attr)
691             if type(option_contain)==type([]):
692                 option_contain = ",".join(option_contain)
693             if type(option_contain)==type(True):
694                 option_contain = ""
695             text_options+= "--%s %s " % (attr, option_contain)
696     return text_options
697                 
698
699 def get_version():
700     '''
701     get colorized salomeTools version (in src/internal_config/salomeTools.pyconf).
702     returns string
703     '''
704     # read the config 
705     cfgManager = CONFIG.ConfigManager()
706     cfg = cfgManager.get_config()
707     # print the key corresponding to salomeTools version
708     msg = (src.printcolors.printcHeader( _("Version: ") ) + src.get_salometool_version(cfg))
709     return msg
710
711
712 def get_help():
713     '''
714     get salomeTools general help.
715     returns string
716     '''
717     msg = "\n" + get_version() + "\n\n"
718     msg += src.printcolors.printcHeader( _("Usage: ") ) + \
719           "sat [sat_options] <command> [application] [command_options]\n\n"
720
721     msg += parser.get_help() + "\n"
722
723     # display all the available commands.
724     msg += src.printcolors.printcHeader(_("Available commands are:")) + "\n"
725     for command in lCommand:
726         msg += " - %s\n" % (command)
727
728     msg += "\n"
729     # Explain how to get the help for a specific command
730     msg += src.printcolors.printcHeader(
731         _("Get help for a specific command:")) + \
732         "\n>> sat --help <command>\n"
733     return msg
734
735 def write_exception(exc):
736     '''write exception in case of error in a command
737     
738     :param exc exception: the exception to print
739     '''
740     sys.stderr.write("\n***** ")
741     sys.stderr.write(src.printcolors.printcError("salomeTools ERROR:"))
742     sys.stderr.write("\n" + str(exc) + "\n")
743
744