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