]> SALOME platform Git repositories - tools/sat.git/commitdiff
Salome HOME
rm src/logger.py src/printcolors.py
authorChristian Van Wambeke <chvw@orange.fr>
Sun, 25 Mar 2018 19:03:17 +0000 (21:03 +0200)
committerChristian Van Wambeke <chvw@orange.fr>
Sun, 25 Mar 2018 19:03:17 +0000 (21:03 +0200)
15 files changed:
.spyderproject [deleted file]
commands/config.py
sat
src/__init__.py
src/coloringSat.py [new file with mode: 0755]
src/example/__init__.py [new file with mode: 0644]
src/example/essai_logging_1.py [new file with mode: 0755]
src/example/essai_logging_2.py [new file with mode: 0755]
src/exceptionSat.py [new file with mode: 0644]
src/logger.py [deleted file]
src/loggingSat.py [new file with mode: 0755]
src/printcolors.py [deleted file]
src/salomeTools.py
src/salomeTools_old.py [deleted file]
src/utilsSat.py [new file with mode: 0644]

diff --git a/.spyderproject b/.spyderproject
deleted file mode 100644 (file)
index cfa47ec..0000000
Binary files a/.spyderproject and /dev/null differ
index 495e03e936551024304abfe7e8169c20b5686766..515c2a6e1fdb8f30ca3e37cd6fe4530750cfe5f0 100644 (file)
@@ -185,7 +185,7 @@ class ConfigManager:
         '''        
         
         # create a ConfigMerger to handle merge
-        merger = src.pyconf.ConfigMerger()#MergeHandler())
+        merger = src.pyconf.ConfigMerger() #MergeHandler())
         
         # create the configuration instance
         cfg = src.pyconf.Config()
diff --git a/sat b/sat
index cca09c1def098661649fb3a2283a8069bbfd3a53..d374a93f21786f98eeceafae8352ca5bcc124c80 100755 (executable)
--- a/sat
+++ b/sat
 #  License along with this library; if not, write to the Free Software
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-'''This file is the main entry file to use salomeTools
+'''\
+This file is the main entry file to use salomeTools,
+in mode Command Line Argument(s) (CLI)
 '''
 
 import os
 import sys
 
+# OKSYS and KOSYS seems equal on linux or windows
+OKSYS = 0  # OK 
+KOSYS = 1  # KO
+
 # get path to salomeTools sources
 satdir  = os.path.dirname(os.path.realpath(__file__))
 
@@ -34,18 +40,22 @@ sys.path.insert(0, satdir)
 # MAIN
 #################################
 if __name__ == "__main__":
-    import src
+    import src.loggingSat as LOG
+    import src.debug as DBG # Easy print stderr (for DEBUG only)from src.salomeTools import Sat
     from src.salomeTools import Sat
-    import src.debug as DBG # Easy print stderr (for DEBUG only)
-
-    _debug = False          # Have to be False in production (for DEBUG only)
+    
+    _debug = True # Have to be False in production (for DEBUG only)
     DBG.push_debug(_debug)
 
-    # instantiate the salomeTools class with correct options
-    sat = Sat(sys.argv[1:])
-    # or sat = Sat("config -l") for example
-    returnCode = sat.execute_command()
-    DBG.write("sat exit code", returnCode)
+    logger = LOG.getDefaultLogger()
+    # instantiate the salomeTools class
+    sat = Sat(logger)
+    returnCode = sat.execute_cli(sys.argv[1:])
+    logger.debug("sat exit code: %s" % returnCode)
 
     sys.exit(returnCode.toSys())
-        
+
+else:
+    sys.stderr.write("\nERROR: unexpected mode __name__ '%s'" % __name__)
+    sys.exit(KOSYS)
+            
index 76b6d824ff579822c8d47370db2200902b784c58..5ac98048d595e6f8c7153e556d8355146fac982e 100644 (file)
@@ -1,418 +1,2 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-import os
-import shutil
-import errno
-import stat
-import platform
-
-import pyconf
-import architecture
-import printcolors
-import options
-import system
-import ElementTree
-import logger
-import product
-import environment
-import fileEnviron
-import compilation
-import test_module
-import template
-
-if platform.system() == "Windows" :
-    import colorama
-    colorama.init()
-
-
-CONFIG_FILENAME = "sat-config.pyconf"
-
-
-class SatException(Exception):
-    '''rename Exception Class
-    '''
-    pass
-
-def ensure_path_exists(p):
-    '''Create a path if not existing
-    
-    :param p str: The path.
-    '''
-    if not os.path.exists(p):
-        os.makedirs(p)
-        
-def check_config_has_application( config, details = None ):
-    '''check that the config has the key APPLICATION. Else raise an exception.
-    
-    :param config class 'common.pyconf.Config': The config.
-    '''
-    if 'APPLICATION' not in config:
-        message = _("An APPLICATION is required. Use 'config --list' to get"
-                    " the list of available applications.\n")
-        if details :
-            details.append(message)
-        raise SatException( message )
-
-def check_config_has_profile( config, details = None ):
-    '''check that the config has the key APPLICATION.profile.
-       Else, raise an exception.
-    
-    :param config class 'common.pyconf.Config': The config.
-    '''
-    check_config_has_application(config)
-    if 'profile' not in config.APPLICATION:
-        message = _("A profile section is required in your application.\n")
-        if details :
-            details.append(message)
-        raise SatException( message )
-
-def config_has_application( config ):
-    return 'APPLICATION' in config
-
-def get_cfg_param(config, param_name, default):
-    '''Search for param_name value in config.
-       If param_name is not in config, then return default,
-       else, return the found value
-       
-    :param config class 'common.pyconf.Config': The config.
-    :param param_name str: the name of the parameter to get the value
-    :param default str: The value to return if param_name is not in config
-    :return: see initial description of the function
-    :rtype: str
-    '''
-    if param_name in config:
-        return config[param_name]
-    return default
-
-def print_info(logger, info):
-    '''Prints the tuples that are in info variable in a formatted way.
-    
-    :param logger Logger: The logging instance to use for the prints.
-    :param info list: The list of tuples to display
-    '''
-    # find the maximum length of the first value of the tuples in info
-    smax = max(map(lambda l: len(l[0]), info))
-    # Print each item of info with good indentation
-    for i in info:
-        sp = " " * (smax - len(i[0]))
-        printcolors.print_value(logger, sp + i[0], i[1], 2)
-    logger.write("\n", 2)
-
-def get_base_path(config):
-    '''Returns the path of the products base.
-    
-    :param config Config: The global Config instance.
-    :return: The path of the products base.
-    :rtype: str
-    '''
-    if "base" not in config.LOCAL:
-        local_file_path = os.path.join(config.VARS.salometoolsway,
-                                      "data",
-                                      "local.pyconf")
-        msg = _("Please define a base path in the file %s") % local_file_path
-        raise SatException(msg)
-        
-    base_path = os.path.abspath(config.LOCAL.base)
-    
-    return base_path
-
-def get_launcher_name(config):
-    '''Returns the name of salome launcher.
-    
-    :param config Config: The global Config instance.
-    :return: The name of salome launcher.
-    :rtype: str
-    '''
-    check_config_has_application(config)
-    if 'profile' in config.APPLICATION and 'launcher_name' in config.APPLICATION.profile:
-        launcher_name = config.APPLICATION.profile.launcher_name
-    else:
-        launcher_name = 'salome'
-
-    return launcher_name
-
-def get_log_path(config):
-    '''Returns the path of the logs.
-    
-    :param config Config: The global Config instance.
-    :return: The path of the logs.
-    :rtype: str
-    '''
-    if "log_dir" not in config.LOCAL:
-        local_file_path = os.path.join(config.VARS.salometoolsway,
-                                      "data",
-                                      "local.pyconf")
-        msg = _("Please define a log_dir in the file %s") % local_file_path
-        raise SatException(msg)
-      
-    log_dir_path = os.path.abspath(config.LOCAL.log_dir)
-    
-    return log_dir_path
-
-def get_salome_version(config):
-    if hasattr(config.APPLICATION, 'version_salome'):
-        Version = config.APPLICATION.version_salome
-    else:
-        KERNEL_info = product.get_product_config(config, "KERNEL")
-        VERSION = os.path.join(
-                            KERNEL_info.install_dir,
-                            "bin",
-                            "salome",
-                            "VERSION")
-        if not os.path.isfile(VERSION):
-            return None
-            
-        fVERSION = open(VERSION)
-        Version = fVERSION.readline()
-        fVERSION.close()
-        
-    VersionSalome = int(only_numbers(Version))    
-    return VersionSalome
-
-def only_numbers(str_num):
-    return ''.join([nb for nb in str_num if nb in '0123456789'] or '0')
-
-def read_config_from_a_file(filePath):
-        try:
-            cfg_file = pyconf.Config(filePath)
-        except pyconf.ConfigError as e:
-            raise SatException(_("Error in configuration file: %(file)s\n  %(error)s") %
-                { 'file': filePath, 'error': str(e) } )
-        return cfg_file
-
-def get_tmp_filename(cfg, name):
-    if not os.path.exists(cfg.VARS.tmp_root):
-        os.makedirs(cfg.VARS.tmp_root)
-
-    return os.path.join(cfg.VARS.tmp_root, name)
-
-##
-# Utils class to simplify path manipulations.
-class Path:
-    def __init__(self, path):
-        self.path = str(path)
-
-    def __add__(self, other):
-        return Path(os.path.join(self.path, str(other)))
-
-    def __abs__(self):
-        return Path(os.path.abspath(self.path))
-
-    def __str__(self):
-        return self.path
-
-    def __eq__(self, other):
-        return self.path == other.path
-
-    def exists(self):
-        return self.islink() or os.path.exists(self.path)
-
-    def islink(self):
-        return os.path.islink(self.path)
-
-    def isdir(self):
-        return os.path.isdir(self.path)
-
-    def isfile(self):
-        return os.path.isfile(self.path)
-
-    def list(self):
-        return [Path(p) for p in os.listdir(self.path)]
-
-    def dir(self):
-        return Path(os.path.dirname(self.path))
-
-    def base(self):
-        return Path(os.path.basename(self.path))
-
-    def make(self, mode=None):
-        os.makedirs(self.path)        
-        if mode:
-            os.chmod(self.path, mode)
-        
-    def chmod(self, mode):
-        os.chmod(self.path, mode)
-
-    def rm(self):    
-        if self.islink():
-            os.remove(self.path)
-        else:
-            shutil.rmtree( self.path, onerror = handleRemoveReadonly )
-
-    def copy(self, path, smart=False):
-        if not isinstance(path, Path):
-            path = Path(path)
-
-        if os.path.islink(self.path):
-            return self.copylink(path)
-        elif os.path.isdir(self.path):
-            return self.copydir(path, smart)
-        else:
-            return self.copyfile(path)
-
-    def smartcopy(self, path):
-        return self.copy(path, True)
-
-    def readlink(self):
-        if self.islink():
-            return os.readlink(self.path)
-        else:
-            return False
-
-    def symlink(self, path):
-        try:
-            os.symlink(str(path), self.path)
-            return True
-        except:
-            return False
-
-    def copylink(self, path):
-        try:
-            os.symlink(os.readlink(self.path), str(path))
-            return True
-        except:
-            return False
-
-    def copydir(self, dst, smart=False):
-        try:
-            names = self.list()
-
-            if not dst.exists():
-                dst.make()
-
-            for name in names:
-                if name == dst:
-                    continue
-                if smart and (str(name) in [".git", "CVS", ".svn"]):
-                    continue
-                srcname = self + name
-                dstname = dst + name
-                srcname.copy(dstname, smart)
-            return True
-        except:
-            return False
-
-    def copyfile(self, path):
-        try:
-            shutil.copy2(self.path, str(path))
-            return True
-        except:
-            return False
-
-def find_file_in_lpath(file_name, lpath, additional_dir = ""):
-    """Find in all the directories in lpath list the file that has the same name
-       as file_name. If it is found, return the full path of the file, else,
-       return False. 
-       The additional_dir (optional) is the name of the directory to add to all 
-       paths in lpath.
-    
-    :param file_name str: The file name to search
-    :param lpath List: The list of directories where to search
-    :param additional_dir str: The name of the additional directory
-    :return: the full path of the file or False if not found
-    :rtype: str
-    """
-    for directory in lpath:
-        dir_complete = os.path.join(directory, additional_dir)
-        if not os.path.isdir(directory) or not os.path.isdir(dir_complete):
-            continue
-        l_files = os.listdir(dir_complete)
-        for file_n in l_files:
-            if file_n == file_name:
-                return os.path.join(dir_complete, file_name)
-    return False
-
-def handleRemoveReadonly(func, path, exc):
-    excvalue = exc[1]
-    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
-        os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
-        func(path)
-    else:
-        raise
-
-def deepcopy_list(input_list):
-    """ Do a deep copy of a list
-    
-    :param input_list List: The list to copy
-    :return: The copy of the list
-    :rtype: List
-    """
-    res = []
-    for elem in input_list:
-        res.append(elem)
-    return res
-
-def remove_item_from_list(input_list, item):
-    """ Remove all occurences of item from input_list
-    
-    :param input_list List: The list to modify
-    :return: The without any item
-    :rtype: List
-    """
-    res = []
-    for elem in input_list:
-        if elem == item:
-            continue
-        res.append(elem)
-    return res
-
-def parse_date(date):
-    """Transform YYYYMMDD_hhmmss into YYYY-MM-DD hh:mm:ss.
-    
-    :param date str: The date to transform
-    :return: The date in the new format
-    :rtype: str
-    """
-    if len(date) != 15:
-        return date
-    res = "%s-%s-%s %s:%s:%s" % (date[0:4],
-                                 date[4:6],
-                                 date[6:8],
-                                 date[9:11],
-                                 date[11:13],
-                                 date[13:])
-    return res
-
-def merge_dicts(*dict_args):
-    '''
-    Given any number of dicts, shallow copy and merge into a new dict,
-    precedence goes to key value pairs in latter dicts.
-    '''
-    result = {}
-    for dictionary in dict_args:
-        result.update(dictionary)
-    return result
-
-def replace_in_file(filein, strin, strout):
-    '''Replace <strin> by <strout> in file <filein>
-    '''
-    shutil.move(filein, filein + "_old")
-    fileout= filein
-    filein = filein + "_old"
-    fin = open(filein, "r")
-    fout = open(fileout, "w")
-    for line in fin:
-        fout.write(line.replace(strin, strout))
-
-def get_property_in_product_cfg(product_cfg, pprty):
-    if not "properties" in product_cfg:
-        return None
-    if not pprty in product_cfg.properties:
-        return None
-    return product_cfg.properties[pprty]
+import src.pyconf
\ No newline at end of file
diff --git a/src/coloringSat.py b/src/coloringSat.py
new file mode 100755 (executable)
index 0000000..8fc13c6
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+simple tagging as '<color>' for simple coloring log messages on terminal(s)
+window or unix or ios using backend colorama
+
+using '<color>' because EZ human readable
+so '<color>' are not supposed existing in log message
+"{}".format() is not choosen because "{}" are present
+in log messages of contents of python dict (as JSON) etc.
+
+example:
+>> log("this is in <green>color green<reset>, OK is in blue: <blue>OK?")
+"""
+
+import os
+import sys
+import pprint as PP
+
+_verbose = True
+_name = "coloringSat"
+
+""" 
+https://github.com/tartley/colorama
+init(wrap=True):
+
+On Windows, colorama works by replacing sys.stdout and sys.stderr 
+with proxy objects, which override the .write() method to do their work. 
+If this wrapping causes you problems, 
+then this can be disabled by passing init(wrap=False). 
+The default behaviour is to wrap if autoreset or strip or convert are True.
+
+When wrapping is disabled, colored printing on non-Windows platforms 
+will continue to work as normal. 
+To do cross-platform colored output, 
+you can use Colorama's AnsiToWin32 proxy directly:
+
+example:
+  import sys
+  from colorama import init, AnsiToWin32, Fore
+  init(wrap=False)
+  stream = AnsiToWin32(sys.stderr).stream
+  # Python 2
+  print >>stream, Fore.BLUE + 'blue text on stderr'
+  # Python 3
+  print(Fore.BLUE + 'blue text on stderr', file=stream)
+"""
+
+import colorama as CLRM
+from colorama import Fore as FG
+from colorama import Style as ST
+#from colorama import AnsiToWin32
+from ansitowin32 import AnsiToWin32 # debug is os.name == 'nt' ?
+
+CLRM.init(wrap=False) # choose NO wrapping
+
+
+"""
+from colorama:
+Available formatting constants are:
+Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
+Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
+Style: DIM, NORMAL, BRIGHT, RESET_ALL
+
+n.b. DIM is not assumed in win32
+"""
+dir(ST)
+# order matters for replace
+_tags = (
+  ("<black>", FG.BLACK),
+  ("<red>", FG.RED),
+  ("<green>", FG.GREEN),
+  ("<yellow>", FG.YELLOW),
+  ("<blue>", FG.BLUE),
+  ("<magenta>", FG.MAGENTA),
+  ("<cyan>", FG.CYAN),
+  ("<white>", FG.WHITE),
+  ("<bright>", ST.BRIGHT),
+  ("<normal>", ST.NORMAL),
+  ("<reset>", ST.RESET_ALL),
+  ("<OK>", FG.GREEN + ST.BRIGHT + "OK" + ST.RESET_ALL),
+  ("<KO>", FG.RED + ST.BRIGHT + "KO" + ST.RESET_ALL),
+)
+
+# _tagsNone = ((i, "") for i,j in _tags) # to clean tags when log not tty
+_tagsNone = (
+  ("<black>", ""),
+  ("<red>", ""),
+  ("<green>", ""),
+  ("<yellow>", ""),
+  ("<blue>", ""),
+  ("<magenta>", ""),
+  ("<cyan>", ""),
+  ("<white>", ""),
+  ("<bright>", ""),
+  ("<normal>", ""),
+  ("<reset>", ""),
+  ("<OK>", "OK"),
+  ("<KO>", "KO"),
+)
+
+def indent(msg, nb, car=" "):
+  """indent nb car (spaces) multi lines message except first one"""
+  s = msg.split("\n")
+  res = ("\n"+car*nb).join(s)
+  return res
+
+def log(msg):
+  """elementary log stdout for debug if _verbose"""
+  prefix = "%s.log: " % _name
+  nb = len(prefix)
+  if _verbose: 
+    ini = prefix + indent(msg, nb)
+    res = toColor(ini)
+    if res != ini:
+      res = res + toColor("<reset>")
+    print(res)
+  
+class ColoringStream(object):
+  """
+  write my stream class
+  only write and flush are used for the streaming
+  https://docs.python.org/2/library/logging.handlers.html
+  https://stackoverflow.com/questions/31999627/storing-logger-messages-in-a-string
+  """
+  def __init__(self):
+    self.logs = ''
+
+  def write(self, astr):
+    # log("UnittestStream.write('%s')" % astr)
+    self.logs += astr
+
+  def flush(self):
+    pass
+
+  def __str__(self):
+    return self.logs
+
+def toColor(msg):
+  if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
+    # clean the message color if the terminal is redirected by user
+    # ex: sat compile appli > log.txt
+    return replace(msg, _tagsNone)
+  else:
+    return replace(msg, _tags)
+  
+def toColor_AnsiToWin32(msg):
+  """for test debug no wrapping"""
+  if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
+    # clean the message color if the terminal is redirected by user
+    # ex: sat compile appli > log.txt
+    return replace(msg, _tagsNone)
+  else:
+    msgAnsi = replace(msg, _tags)
+    streamOut = ColoringStream()
+    atw = AnsiToWin32(streamOut, convert=True)
+    streamIn = atw.stream
+    print "should_wrap",atw.should_wrap(),atw.convert,atw.strip,atw.autoreset
+    streamIn.write(msgAnsi)
+    #AnsiToWin32(streamOut).write_and_convert(msgAnsi)
+    # print "streamOut",str(streamOut)
+    return str(streamOut)
+
+def replace(msg, tags):
+  s = msg
+  for r in tags:
+    s = s.replace(*r)
+  return s
+  
+if __name__ == "__main__":  
+  #log(FG.BLUE + 'blue text on stdout'+ ST.RESET_ALL)
+  log("import <green>colorama at <blue>%s" % CLRM.__file__)
+  log("import <green>colorama<reset> in <blue>%s: <OK>" % __file__)
+  log("import <green>colorama<reset> in <blue>%s: <KO>" % __file__)
+  log("import <green>colorama in <blue>%s" % __file__)
+  log("set <green>green and not reset...")
+  log("...and here is not green because appended reset at end of message")
+  log("dir(FG):\n<blue>%s" % dir(FG))
+  log("dir(ST):\n<blue>%s" % dir(ST))
+
+  
\ No newline at end of file
diff --git a/src/example/__init__.py b/src/example/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/example/essai_logging_1.py b/src/example/essai_logging_1.py
new file mode 100755 (executable)
index 0000000..a8c8f88
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+http://sametmax.com/ecrire-des-logs-en-python/
+https://docs.python.org/3/library/time.html#time.strftime
+
+essai utilisation logger plusieurs handler format different
+
+    /usr/lib/python2.7/logging/__init__.pyc
+
+    init MyLogger, fmt='%(asctime)s :: %(levelname)-8s :: %(message)s', level='20'
+
+    2018-03-11 18:51:21 :: INFO     :: test logger info
+    2018-03-11 18:51:21 :: WARNING  :: test logger warning
+    2018-03-11 18:51:21 :: ERROR    :: test logger error
+    2018-03-11 18:51:21 :: CRITICAL :: test logger critical
+
+    init MyLogger, fmt='None', level='10'
+
+    2018-03-11 18:51:21 :: DEBUG    :: test logger debug
+    test logger debug
+    2018-03-11 18:51:21 :: INFO     :: test logger info
+    test logger info
+    2018-03-11 18:51:21 :: WARNING  :: test logger warning
+    test logger warning
+    2018-03-11 18:51:21 :: ERROR    :: test logger error
+    test logger error
+    2018-03-11 18:51:21 :: CRITICAL :: test logger critical
+    test logger critical
+"""
+
+import os
+import sys
+import logging
+import pprint as PP
+
+print logging.__file__
+
+def initMyLogger(fmt=None, level=None):
+  # http://sametmax.com/ecrire-des-logs-en-python/
+  # https://docs.python.org/3/library/time.html#time.strftime
+  print "\ninit MyLogger, fmt='%s', level='%s'\n" % (fmt, level)
+  logger = getMyLogger()
+  handler = logging.StreamHandler() # Logging vers console
+  if fmt is not None:
+    formatter = logging.Formatter(fmt, "%Y-%m-%d %H:%M:%S")
+    handler.setFormatter(formatter)
+  logger.addHandler(handler)
+  if level is not None:
+    logger.setLevel(level)
+  else:
+    logger.setLevel(logger.INFO) # ou DEBUG
+  # logger.info('\n' + PP.pformat(dir(logger)))
+  
+def getMyLogger():
+  return logging.getLogger('MyLogger')
+
+def testLogger1():
+  logger = getMyLogger()
+  logger.debug('test logger debug')
+  logger.info('test logger info')
+  logger.warning('test logger warning')
+  logger.error('test logger error')
+  logger.critical('test logger critical')
+  
+if __name__ == "__main__":
+  initMyLogger('%(asctime)s :: %(levelname)-8s :: %(message)s', level=logging.INFO)
+  testLogger1()
+  initMyLogger(level=logging.DEBUG)
+  testLogger1()
diff --git a/src/example/essai_logging_2.py b/src/example/essai_logging_2.py
new file mode 100755 (executable)
index 0000000..ba5da19
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+http://sametmax.com/ecrire-des-logs-en-python/
+https://docs.python.org/3/library/time.html#time.strftime
+
+essai utilisation logger un handler format different 
+sur info() pas de format et su other format
+
+    /usr/lib/python2.7/logging/__init__.pyc
+
+    init MyLogger, fmt='%(asctime)s :: %(levelname)-8s :: %(message)s', level='20'
+
+    test logger info
+    2018-03-11 18:51:51 :: WARNING  :: test logger warning
+    2018-03-11 18:51:51 :: ERROR    :: test logger error
+    2018-03-11 18:51:51 :: CRITICAL :: test logger critical
+
+"""
+
+import os
+import sys
+import logging
+import pprint as PP
+
+print logging.__file__
+
+class MyFormatter(logging.Formatter):
+  def format(self, record):
+    # print "", record.levelname #type(record), dir(record)
+    if record.levelname == "INFO": 
+      return str(record.msg)
+    else:
+      return super(MyFormatter, self).format(record)
+
+def initMyLogger(fmt=None, level=None):
+  # http://sametmax.com/ecrire-des-logs-en-python/
+  # https://docs.python.org/3/library/time.html#time.strftime
+  print "\ninit MyLogger, fmt='%s', level='%s'\n" % (fmt, level)
+  logger = getMyLogger()
+  handler = logging.StreamHandler() # Logging vers console
+  if fmt is not None:
+    # formatter = logging.Formatter(fmt, "%Y-%m-%d %H:%M:%S")
+    formatter =MyFormatter(fmt, "%Y-%m-%d %H:%M:%S")
+    handler.setFormatter(formatter)
+  logger.addHandler(handler)
+  if level is not None:
+    logger.setLevel(level)
+  else:
+    logger.setLevel(logger.INFO) # ou DEBUG
+  # logger.info('\n' + PP.pformat(dir(logger)))
+  
+def getMyLogger():
+  return logging.getLogger('MyLogger')
+
+def testLogger1():
+  logger = getMyLogger()
+  logger.debug('test logger debug')
+  logger.info('test logger info')
+  logger.warning('test logger warning')
+  logger.error('test logger error')
+  logger.critical('test logger critical')
+  
+if __name__ == "__main__":
+  initMyLogger('%(asctime)s :: %(levelname)-8s :: %(message)s', level=logging.INFO)
+  testLogger1()
+
diff --git a/src/exceptionSat.py b/src/exceptionSat.py
new file mode 100644 (file)
index 0000000..dba733f
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+#  Copyright (C) 2010-2018  CEA/DEN
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+
+class ExceptionSat(Exception):
+    '''rename Exception Class for sat convenience (for future...)
+    '''
+    pass
+
diff --git a/src/logger.py b/src/logger.py
deleted file mode 100644 (file)
index 0a78a7b..0000000
+++ /dev/null
@@ -1,414 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-'''In this file are implemented the classes and method relative to the logging
-'''
-
-import sys
-import os
-import datetime
-import re
-import tempfile
-
-import src
-
-from src import printcolors
-from src import xmlManager
-
-log_macro_command_file_expression = "^[0-9]{8}_+[0-9]{6}_+.*\.xml$"
-log_all_command_file_expression = "^.*[0-9]{8}_+[0-9]{6}_+.*\.xml$"
-
-
-class Logger(object):
-    '''Class to handle log mechanism.
-    '''
-    def __init__(self,
-                 config,
-                 silent_sysstd=False,
-                 all_in_terminal=False,
-                 micro_command = False):
-        '''Initialization
-        
-        :param config pyconf.Config: The global configuration.
-        :param silent_sysstd boolean: if True, do not write anything
-                                      in terminal.
-        '''
-        self.config = config
-        self.default_level = 3
-        self.silentSysStd = silent_sysstd
-        
-        # Construct xml log file location for sat prints.
-        prefix = ""
-        if micro_command:
-            prefix = "micro_"
-        hour_command_host = (config.VARS.datehour + "_" + 
-                             config.VARS.command + "_" + 
-                             config.VARS.hostname)
-        logFileName = prefix + hour_command_host + ".xml"
-        log_dir = src.get_log_path(config)
-        logFilePath = os.path.join(log_dir, logFileName)
-        # Construct txt file location in order to log 
-        # the external commands calls (cmake, make, git clone, etc...)
-        txtFileName = prefix + hour_command_host + ".txt"
-        txtFilePath = os.path.join(log_dir, "OUT", txtFileName)
-        
-        src.ensure_path_exists(os.path.dirname(logFilePath))
-        src.ensure_path_exists(os.path.dirname(txtFilePath))
-        
-        # The path of the log files (one for sat traces, and the other for 
-        # the system commands traces)
-        self.logFileName = logFileName
-        self.logFilePath = logFilePath
-        self.txtFileName = txtFileName
-        self.txtFilePath = txtFilePath
-        
-        # The list of all log files corresponding to the current command and
-        # the commands called by the current command
-        self.l_logFiles = [logFilePath, txtFilePath]
-        
-        # Initialize xml instance and put first fields 
-        # like beginTime, user, command, etc... 
-        self.xmlFile = xmlManager.XmlLogFile(logFilePath, "SATcommand", 
-                            attrib = {"application" : config.VARS.application})
-        self.put_initial_xml_fields()
-        # Initialize the txt file for reading
-        try:
-            self.logTxtFile = open(str(self.txtFilePath), 'w')
-        except IOError:
-            #msg1 = _("WARNING! Trying to write to a file that is not accessible:")
-            #msg2 = _("The logs won't be written.")
-            #print("%s\n%s\n%s\n" % (src.printcolors.printcWarning(msg1),
-            #                        src.printcolors.printcLabel(str(self.txtFilePath)),
-            #                        src.printcolors.printcWarning(msg2) ))
-            self.logTxtFile = tempfile.TemporaryFile()
-            
-        # If the option all_in_terminal was called, all the system commands
-        # are redirected to the terminal
-        if all_in_terminal:
-            self.logTxtFile = sys.__stdout__
-        
-    def put_initial_xml_fields(self):
-        '''Method called at class initialization : Put all fields 
-           corresponding to the command context (user, time, ...)
-        '''
-        # command name
-        self.xmlFile.add_simple_node("Site", attrib={"command" : 
-                                                     self.config.VARS.command})
-        # version of salomeTools
-        self.xmlFile.append_node_attrib("Site", attrib={"satversion" : 
-                                            self.config.INTERNAL.sat_version})
-        # machine name on which the command has been launched
-        self.xmlFile.append_node_attrib("Site", attrib={"hostname" : 
-                                                    self.config.VARS.hostname})
-        # Distribution of the machine
-        self.xmlFile.append_node_attrib("Site", attrib={"OS" : 
-                                                        self.config.VARS.dist})
-        # The user that have launched the command
-        self.xmlFile.append_node_attrib("Site", attrib={"user" : 
-                                                        self.config.VARS.user})
-        # The time when command was launched
-        Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
-        date_hour = "%2s/%2s/%4s %2sh%2sm%2ss" % (dd, m, Y, H, M, S)
-        self.xmlFile.append_node_attrib("Site", attrib={"beginTime" : 
-                                                        date_hour})
-        # The application if any
-        if "APPLICATION" in self.config:
-            self.xmlFile.append_node_attrib("Site", 
-                        attrib={"application" : self.config.VARS.application})
-        # The initialization of the trace node
-        self.xmlFile.add_simple_node("Log",text="")
-        # The system commands logs
-        self.xmlFile.add_simple_node("OutLog",
-                                    text=os.path.join("OUT", self.txtFileName))
-        # The initialization of the node where 
-        # to put the links to the other sat commands that can be called by any
-        # command 
-        self.xmlFile.add_simple_node("Links")
-
-    def add_link(self,
-                 log_file_name,
-                 command_name,
-                 command_res,
-                 full_launched_command):
-        '''Add a link to another log file.
-        
-        :param log_file_name str: The file name of the link.
-        :param command_name str: The name of the command linked.
-        :param command_res str: The result of the command linked. "0" or "1"
-        :parma full_launched_command str: The full lanch command 
-                                          ("sat command ...")
-        '''
-        xmlLinks = self.xmlFile.xmlroot.find("Links")
-        src.xmlManager.add_simple_node(xmlLinks,
-                                       "link", 
-                                       text = log_file_name,
-                                       attrib = {"command" : command_name,
-                                                 "passed" : command_res,
-                                           "launchedCommand" : full_launched_command})
-
-    def write(self, message, level=None, screenOnly=False):
-        '''the function used in the commands 
-        that will print in the terminal and the log file.
-        
-        :param message str: The message to print.
-        :param level int: The output level corresponding 
-                          to the message 0 < level < 6.
-        :param screenOnly boolean: if True, do not write in log file.
-        '''
-        # do not write message starting with \r to log file
-        if not message.startswith("\r") and not screenOnly:
-            self.xmlFile.append_node_text("Log", 
-                                          printcolors.cleancolor(message))
-
-        # get user or option output level
-        current_output_verbose_level = self.config.USER.output_verbose_level
-        if not ('isatty' in dir(sys.stdout) and sys.stdout.isatty()):
-            # clean the message color if the terminal is redirected by user
-            # ex: sat compile appli > log.txt
-            message = printcolors.cleancolor(message)
-        
-        # Print message regarding the output level value
-        if level:
-            if level <= current_output_verbose_level and not self.silentSysStd:
-                sys.stdout.write(message)
-        else:
-            if self.default_level <= current_output_verbose_level and not self.silentSysStd:
-                sys.stdout.write(message)
-        self.flush()
-
-    def error(self, message):
-        '''Print an error.
-        
-        :param message str: The message to print.
-        '''
-        # Print in the log file
-        self.xmlFile.append_node_text("traces", _('ERROR:') + message)
-
-        # Print in the terminal and clean colors if the terminal 
-        # is redirected by user
-        if not ('isatty' in dir(sys.stderr) and sys.stderr.isatty()):
-            sys.stderr.write(printcolors.printcError(_('ERROR:') + message))
-        else:
-            sys.stderr.write(_('ERROR:') + message)
-
-    def flush(self):
-        '''Flush terminal
-        '''
-        sys.stdout.flush()
-        self.logTxtFile.flush()
-        
-    def end_write(self, attribute):
-        '''Method called just after command end : Put all fields 
-           corresponding to the command end context (time).
-           Write the log xml file on the hard drive.
-           And display the command to launch to get the log
-        
-        :param attribute dict: the attribute to add to the node "Site".
-        '''       
-        # Get current time (end of command) and format it
-        dt = datetime.datetime.now()
-        Y, m, dd, H, M, S = date_to_datetime(self.config.VARS.datehour)
-        t0 = datetime.datetime(int(Y), int(m), int(dd), int(H), int(M), int(S))
-        tf = dt
-        delta = tf - t0
-        total_time = timedelta_total_seconds(delta)
-        hours = int(total_time / 3600)
-        minutes = int((total_time - hours*3600) / 60)
-        seconds = total_time - hours*3600 - minutes*60
-        # Add the fields corresponding to the end time
-        # and the total time of command
-        endtime = dt.strftime('%Y/%m/%d %Hh%Mm%Ss')
-        self.xmlFile.append_node_attrib("Site", attrib={"endTime" : endtime})
-        self.xmlFile.append_node_attrib("Site", 
-                attrib={"TotalTime" : "%ih%im%is" % (hours, minutes, seconds)})
-        
-        # Add the attribute passed to the method
-        self.xmlFile.append_node_attrib("Site", attrib=attribute)
-        
-        # Call the method to write the xml file on the hard drive
-        self.xmlFile.write_tree(stylesheet = "command.xsl")
-        
-        # Dump the config in a pyconf file in the log directory
-        logDir = src.get_log_path(self.config)
-        dumpedPyconfFileName = (self.config.VARS.datehour 
-                                + "_" 
-                                + self.config.VARS.command 
-                                + ".pyconf")
-        dumpedPyconfFilePath = os.path.join(logDir, 'OUT', dumpedPyconfFileName)
-        try:
-            f = open(dumpedPyconfFilePath, 'w')
-            self.config.__save__(f)
-            f.close()
-        except IOError:
-            pass
-
-_DefaultLogger = [] # o,nly one default logger as common logger on one config
-
-def getDefaultLogger(config):
-    if len(_DefaultLogger) == 1:
-      if config != _DefaultLogger[0].config:
-        raise Exception("config is not unique, unexpected, have to fix that")
-      return _DefaultLogger[0]
-    if config == None:
-      raise Exception("config have to be set for default logger, not None")
-    _DefaultLogger.append(Logger(config))
-    return _DefaultLogger[0]
-
-def date_to_datetime(date):
-    '''Little method that gets year, mon, day, hour , 
-       minutes and seconds from a str in format YYYYMMDD_HHMMSS
-    
-    :param date str: The date in format YYYYMMDD_HHMMSS
-    :return: the same date and time in separate variables.
-    :rtype: (str,str,str,str,str,str)
-    '''
-    Y = date[:4]
-    m = date[4:6]
-    dd = date[6:8]
-    H = date[9:11]
-    M = date[11:13]
-    S = date[13:15]
-    return Y, m, dd, H, M, S
-
-def timedelta_total_seconds(timedelta):
-    '''Little method to replace total_seconds from 
-       datetime module in order to be compatible with old python versions
-    
-    :param timedelta datetime.timedelta: The delta between two dates
-    :return: The number of seconds corresponding to timedelta.
-    :rtype: float
-    '''
-    return (
-        timedelta.microseconds + 0.0 +
-        (timedelta.seconds + timedelta.days * 24 * 3600) * 10 ** 6) / 10 ** 6
-        
-def show_command_log(logFilePath, cmd, application, notShownCommands):
-    '''Used in updateHatXml. Determine if the log xml file logFilePath 
-       has to be shown or not in the hat log.
-    
-    :param logFilePath str: the path to the command xml log file
-    :param cmd str: the command of the log file
-    :param application str: the application passed as parameter 
-                            to the salomeTools command
-    :param notShownCommands list: the list of commands 
-                                  that are not shown by default
-    
-    :return: True if cmd is not in notShownCommands and the application 
-             in the log file corresponds to application
-    :rtype: boolean
-    '''
-    # When the command is not in notShownCommands, no need to go further :
-    # Do not show
-    if cmd in notShownCommands:
-        return False, None, None
-    # Get the application of the log file
-    try:
-        logFileXml = src.xmlManager.ReadXmlFile(logFilePath)
-    except Exception as e:
-        msg = _("WARNING: the log file %s cannot be read:") % logFilePath
-        sys.stdout.write(printcolors.printcWarning("%s\n%s\n" % (msg, e)))
-        return False, None, None
-
-    if 'application' in logFileXml.xmlroot.keys():
-        appliLog = logFileXml.xmlroot.get('application')
-        launched_cmd = logFileXml.xmlroot.find('Site').attrib['launchedCommand']
-        # if it corresponds, then the log has to be shown
-        if appliLog == application:
-            return True, appliLog, launched_cmd
-        elif application != 'None':
-            return False, appliLog, launched_cmd
-        
-        return True, appliLog, launched_cmd
-    
-    if application == 'None':
-            return True, None, None
-        
-    return False, None, None
-
-def list_log_file(dirPath, expression):
-    '''Find all files corresponding to expression in dirPath
-    
-    :param dirPath str: the directory where to search the files
-    :param expression str: the regular expression of files to find
-    :return: the list of files path and informations about it
-    :rtype: list
-    '''
-    lRes = []
-    for fileName in os.listdir(dirPath):
-        # YYYYMMDD_HHMMSS_namecmd.xml
-        sExpr = expression
-        oExpr = re.compile(sExpr)
-        if oExpr.search(fileName):
-            file_name = fileName
-            if fileName.startswith("micro_"):
-                file_name = fileName[len("micro_"):]
-            # get date and hour and format it
-            date_hour_cmd_host = file_name.split('_')
-            date_not_formated = date_hour_cmd_host[0]
-            date = "%s/%s/%s" % (date_not_formated[6:8], 
-                                 date_not_formated[4:6], 
-                                 date_not_formated[0:4])
-            hour_not_formated = date_hour_cmd_host[1]
-            hour = "%s:%s:%s" % (hour_not_formated[0:2], 
-                                 hour_not_formated[2:4], 
-                                 hour_not_formated[4:6])
-            if len(date_hour_cmd_host) < 4:
-                cmd = date_hour_cmd_host[2][:-len('.xml')]
-                host = ""
-            else:
-                cmd = date_hour_cmd_host[2]
-                host = date_hour_cmd_host[3][:-len('.xml')]
-            lRes.append((os.path.join(dirPath, fileName), 
-                         date_not_formated,
-                         date,
-                         hour_not_formated,
-                         hour,
-                         cmd,
-                         host))
-    return lRes
-
-def update_hat_xml(logDir, application=None, notShownCommands = []):
-    '''Create the xml file in logDir that contain all the xml file 
-       and have a name like YYYYMMDD_HHMMSS_namecmd.xml
-    
-    :param logDir str: the directory to parse
-    :param application str: the name of the application if there is any
-    '''
-    # Create an instance of XmlLogFile class to create hat.xml file
-    xmlHatFilePath = os.path.join(logDir, 'hat.xml')
-    xmlHat = src.xmlManager.XmlLogFile(xmlHatFilePath,
-                                    "LOGlist", {"application" : application})
-    # parse the log directory to find all the command logs, 
-    # then add it to the xml file
-    lLogFile = list_log_file(logDir, log_macro_command_file_expression)
-    for filePath, __, date, __, hour, cmd, __ in lLogFile:
-        showLog, cmdAppli, full_cmd = show_command_log(filePath, cmd,
-                                              application, notShownCommands)
-        #if cmd not in notShownCommands:
-        if showLog:
-            # add a node to the hat.xml file
-            xmlHat.add_simple_node("LogCommand", 
-                                   text=os.path.basename(filePath), 
-                                   attrib = {"date" : date, 
-                                             "hour" : hour, 
-                                             "cmd" : cmd, 
-                                             "application" : cmdAppli,
-                                             "full_command" : full_cmd})
-    
-    # Write the file on the hard drive
-    xmlHat.write_tree('hat.xsl')
diff --git a/src/loggingSat.py b/src/loggingSat.py
new file mode 100755 (executable)
index 0000000..2ac44a0
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+http://sametmax.com/ecrire-des-logs-en-python/
+https://docs.python.org/3/library/time.html#time.strftime
+
+use logging package for salometools
+
+handler:
+  on info() no format
+  on other formatted indented on multi lines messages
+"""
+
+import os
+import sys
+import logging
+import pprint as PP
+
+_verbose = False
+_name = "loggingSat"
+
+def indent(msg, nb, car=" "):
+  """indent nb car (spaces) multi lines message except first one"""
+  s = msg.split("\n")
+  res = ("\n"+car*nb).join(s)
+  return res
+
+def log(msg):
+  """elementary log when no logger yet"""
+  prefix = "%s.log: " % _name
+  nb = len(prefix)
+  if _verbose: print(prefix + indent(msg, nb))
+
+log("import logging on %s" % logging.__file__)
+
+_loggerDefaultName = 'SatDefaultLogger'
+_loggerUnittestName = 'SatUnittestLogger'
+
+def getDefaultLogger():
+  log("getDefaultLogger %s" % _loggerDefaultName)
+  return logging.getLogger(_loggerDefaultName)
+
+def getUnittestLogger():
+  log("getUnittestLogger %s" % _loggerUnittestName)
+  return logging.getLogger(_loggerUnittestName)
+
+def dirLogger(logger):
+  logger.info('dir(logger name=%s):\n' % logger.name + PP.pformat(dir(logger)))
+
+_loggerDefault = getDefaultLogger()
+_loggerUnittest = getUnittestLogger()
+
+
+class DefaultFormatter(logging.Formatter):
+  def format(self, record):
+    # print "", record.levelname #type(record), dir(record)
+    if record.levelname == "INFO": 
+      return str(record.msg)
+    else:
+      return indent(super(DefaultFormatter, self).format(record), 12)
+
+class UnittestFormatter(logging.Formatter):
+  def format(self, record):
+    # print "", record.levelname #type(record), dir(record)
+    nb = len("2018-03-17 12:15:41 :: INFO     :: ")
+    return indent(super(UnittestFormatter, self).format(record), nb)
+
+
+class UnittestStream(object):
+  """
+  write my stream class
+  only write and flush are used for the streaming
+  https://docs.python.org/2/library/logging.handlers.html
+  https://stackoverflow.com/questions/31999627/storing-logger-messages-in-a-string
+  """
+  def __init__(self):
+    self.logs = ''
+
+  def write(self, astr):
+    # log("UnittestStream.write('%s')" % astr)
+    self.logs += astr
+
+  def flush(self):
+    pass
+
+  def __str__(self):
+    return self.logs
+
+
+def initLoggerAsDefault(logger, fmt=None, level=None):
+  """
+  init logger as prefixed message and indented message if multi line
+  exept info() outed 'as it' without any format
+  """
+  log("initLoggerAsDefault name=%s\nfmt='%s' level='%s'" % (logger.name, fmt, level))
+  handler = logging.StreamHandler() # Logging vers console
+  if fmt is not None:
+    # formatter = logging.Formatter(fmt, "%Y-%m-%d %H:%M:%S")
+    formatter = DefaultFormatter(fmt, "%y-%m-%d %H:%M:%S")
+    handler.setFormatter(formatter)
+  logger.addHandler(handler)
+  if level is not None:
+    logger.setLevel(level)
+  else:
+    logger.setLevel(logger.INFO)
+
+  
+def initLoggerAsUnittest(logger, fmt=None, level=None):
+  """
+  init logger as silent on stdout/stderr
+  used for retrieve messages in memory for post execution unittest
+  https://docs.python.org/2/library/logging.handlers.html
+  """
+  log("initLoggerAsUnittest name=%s\nfmt='%s' level='%s'" % (logger.name, fmt, level))
+  stream = UnittestStream()
+  handler = logging.StreamHandler(stream) # Logging vers stream
+  if fmt is not None:
+    # formatter = logging.Formatter(fmt, "%Y-%m-%d %H:%M:%S")
+    formatter = UnittestFormatter(fmt, "%Y-%m-%d %H:%M:%S")
+    handler.setFormatter(formatter)
+  logger.addHandler(handler)
+  logger.streamUnittest = stream
+  if level is not None:
+    logger.setLevel(level)
+  else:
+    logger.setLevel(logger.DEBUG)
+
+  
+def testLogger_1(logger):
+  """small test"""
+  # dirLogger(logger)
+  logger.debug('test logger debug')
+  logger.info('test logger info')
+  logger.warning('test logger warning')
+  logger.error('test logger error')
+  logger.critical('test logger critical')
+  logger.info('test logger info:\n- second line\n- third line')
+  logger.warning('test logger warning:\n- second line\n- third line')
+
+  
+if __name__ == "__main__":
+  print("\n**** DEFAULT")
+  logdef = getDefaultLogger()
+  initLoggerAsDefault(logdef, '%(levelname)-8s :: %(message)s', level=logging.INFO)
+  testLogger_1(logdef)
+  print("\n**** UNITTEST")
+  loguni = getUnittestLogger()
+  initLoggerAsUnittest(loguni, '%(asctime)s :: %(levelname)-8s :: %(message)s', level=logging.DEBUG)
+  testLogger_1(loguni) # is silent
+  # log("loguni.streamUnittest:\n%s" % loguni.streamUnittest)
+  print("loguni.streamUnittest:\n%s" % loguni.streamUnittest)
+  
+  from colorama import Fore as FG
+  from colorama import Style as ST
+  print("this is %scolored%s!" % (FG.G))
+  
\ No newline at end of file
diff --git a/src/printcolors.py b/src/printcolors.py
deleted file mode 100644 (file)
index 52d41f6..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-'''In this file is stored the mechanism that manage color prints in the terminal
-'''
-
-# define constant to use in scripts
-COLOR_ERROR = 'ERROR'
-COLOR_WARNING = 'WARNING'
-COLOR_SUCCESS = 'SUCCESS'
-COLOR_LABEL = 'LABEL'
-COLOR_HEADER = 'HEADER'
-COLOR_INFO = 'INFO'
-COLOR_HIGLIGHT = 'HIGHLIGHT'
-
-# the color map to use to print the colors
-__colormap__ = {
-    COLOR_ERROR: '\033[1m\033[31m',
-    COLOR_SUCCESS: '\033[1m\033[32m',
-    COLOR_WARNING: '\033[33m',
-    COLOR_HEADER: '\033[34m',
-    COLOR_INFO: '\033[35m',
-    COLOR_LABEL: '\033[36m',
-    COLOR_HIGLIGHT: '\033[97m\033[43m'
-}
-
-# list of available codes
-__code_range__ = ([1, 4] + list(range(30, 38)) + list(range(40, 48))
-                + list(range(90, 98)) + list(range(100, 108)))
-
-def printc(txt, code=''):
-    '''print a text with colors
-    
-    :param txt str: The text to be printed.
-    :param code str: The color to use.
-    :return: The colored text.
-    :rtype: str
-    '''
-    # no code means 'auto mode' (works only for OK, KO, NO and ERR*)
-    if code == '':
-        striptxt = txt.strip().upper()
-        if striptxt == "OK":
-            code = COLOR_SUCCESS
-        elif striptxt in ["KO", "NO"] or striptxt.startswith("ERR"):
-            code = COLOR_ERROR
-        else:
-            return txt
-
-    # no code => output the originial text
-    if code not in __colormap__.keys() or __colormap__[code] == '':
-        return txt
-
-    return __colormap__[code] + txt + '\033[0m'
-
-def printcInfo(txt):
-    '''print a text info color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_INFO)
-
-def printcError(txt):
-    '''print a text error color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_ERROR)
-
-def printcWarning(txt):
-    '''print a text warning color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_WARNING)
-
-def printcHeader(txt):
-    '''print a text header color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_HEADER)
-
-def printcLabel(txt):
-    '''print a text label color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_LABEL)
-
-def printcSuccess(txt):
-    '''print a text success color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_SUCCESS)
-
-def printcHighlight(txt):
-    '''print a text highlight color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_HIGLIGHT)
-
-def cleancolor(message):
-    '''remove color from a colored text.
-    
-    :param message str: The text to be cleaned.
-    :return: The cleaned text.
-    :rtype: str
-    '''
-    if message == None:
-        return message
-    
-    message = message.replace('\033[0m', '')
-    for i in __code_range__:
-        message = message.replace('\033[%dm' % i, '')
-    return message
-
-def print_value(logger, label, value, level=1, suffix=""):
-    '''shortcut method to print a label and a value with the info color
-    
-    :param logger class logger: the logger instance.
-    :param label int: the label to print.
-    :param value str: the value to print.
-    :param level int: the level of verboseness.
-    :param suffix str: the suffix to add at the end.
-    '''
-    if logger is None:
-        print("  %s = %s %s" % (label, printcInfo(str(value)), suffix))
-    else:
-        logger.write("  %s = %s %s\n" % (label, printcInfo(str(value)),
-                                          suffix), level)
-
-def print_color_range(start, end):
-    '''print possible range values for colors
-    
-    :param start int: The smaller value.
-    :param end int: The bigger value.
-    '''
-    for k in range(start, end+1):
-        print("\033[%dm%3d\033[0m" % (k, k),)
-    print
-
-# This method prints the color map
-def print_color_map():
-    '''This method prints the color map
-    '''
-    print("colormap:")
-    print("{")
-    for k in sorted(__colormap__.keys()):
-        codes = __colormap__[k].split('\033[')
-        codes = filter(lambda l: len(l) > 0, codes)
-        codes = map(lambda l: l[:-1], codes)
-        print(printc("  %s: '%s', " % (k, ';'.join(codes)), k))
-    print("}")
-
-
index 6d2d033a0203ace44569dfe0bf4ef73f2a35c99e..a009fdb136ec18946bde3ee1f00b4ae0a2a35a82 100755 (executable)
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
 '''This file is the main entry file to salomeTools
+NO __main__ entry allowed, use sat
 '''
 
+########################################################################
+# NO __main__ entry allowed, use sat
+########################################################################
+if __name__ == "__main__":
+    sys.stderr.write("\nERROR: 'salomeTools.py' is not main command entry for sat: use 'sat' instead.\n\n")
+    KOSYS = 1 # avoid import src
+    sys.exit(KOSYS)
+
 import os
 import sys
 import re
@@ -30,19 +39,9 @@ import traceback
 import subprocess as SP
 import pprint as PP
 
-
-########################################################################
-# NOT MAIN entry allowed, use sat
-########################################################################
-if __name__ == "__main__":
-    sys.stderr.write("\nERROR: 'salomeTools.py' is not main command entry for sat: use 'sat' instead.\n\n")
-    KOSYS = 1 # avoid import src
-    sys.exit(KOSYS)
-
-
 import src.debug as DBG # Easy print stderr (for DEBUG only)
 import src.returnCode as RCO # Easy (ok/ko, why) return methods code
-import src
+from src.exceptionSat import ExceptionSat
 
 # get path to src
 rootdir = os.path.realpath( os.path.join(os.path.dirname(__file__), "..") )
@@ -120,9 +119,9 @@ def setLocale():
 # _BaseCmd class
 ########################################################################
 class _BaseCommand(object):
-    '''_BaseCmd is the base class for all inherited commands of salomeTools
+    """_BaseCmd is the base class for all inherited commands of salomeTools
     instancied as classes 'Command' in files '.../commands/*.py'
-    '''
+    """
     def __init__(self, name):
         self.name = name
         self.runner = None # runner (as caller) usually as Sat instance
@@ -359,45 +358,37 @@ class _BaseCommand(object):
 # Sat class
 ########################################################################
 class Sat(object):
-    '''The main class that stores all the commands of salomeTools
-    '''
-    def __init__(self, opt='', datadir=None):
-        '''Initialization
+    """The main class that stores all the commands of salomeTools
+    """
+    def __init__(self, logger):
+        """Initialization
         
-        :param opt str or list: The sat options 
-        :param: datadir str : the directory that contain all the external 
-                              data (like software pyconf and software scripts)
-        '''
+        :param logger: The logger to use
+        """
+
         # Read the salomeTools prefixes options before the 'commands' tag
         # sat <options> <args>
-        # (the list of possible options is  at the beginning of this file)
+        # (the list of possible options is at the beginning of this file)
         
-        # DBG.push_debug(True)
-
-        self.parser = self._getParser() 
-        try:
-            if type(opt) is not list: # as string 'sat --help' for example'
-                opts = opt.split()
-            else:
-                opts = opt
-            options, args = self.parser.parse_args(opts)
-            DBG.write("Sat options", options)
-            DBG.write("Sat remainders args", args)
-               
-        except Exception as exc:
-            write_exception(exc)
-            sys.exit(RCO.KOSYS)
 
+        self.CONFIG_FILENAME = "sat-config.pyconf"
+        
         self.config = None # the config that will be read using pyconf module
         self.logger = None # the logger that will be use
-        self.arguments = args # args are postfixes options: args[0] is the 'commands' command
-        self.options = options # the options passed to salomeTools
-        self.datadir = datadir # default value will be <salomeTools root>/data
+        self.arguments = None # args are postfixes options: args[0] is the 'commands' command
+        self.options = None # the options passed to salomeTools
+        
+        # the directory that contain all the external 
+        # data (like software pyconf and software scripts)
+        self.datadir = None # default value will be <salomeTools root>/data
+        
         # contains commands classes needed (think micro commands)
         # if useful 'a la demande'
         self.commands = {}
         self.nameAppliLoaded = None
         
+        self.parser = self._getParser()
+                
     def __repr__(self):
         aDict = {
           "arguments": self.arguments, 
@@ -408,15 +399,23 @@ class Sat(object):
         tmp = PP.pformat(aDict)
         res = "Sat(\n %s\n)\n" % tmp[1:-1]
         return res
-
     
     def getLogger(self):
         if self.logger is None: # could use owner Sat instance logger
-          import src.logger as LOG
-          self.logger=LOG.getDefaultLogger(self.config)
+          import src.loggingSat as LOG
+          self.logger=LOG.getDefaultLogger()
+          self.logger.error("Sat logger not set, fixed as default")
           return self.logger
         else:                   # could use local logger
           return self.logger
+        
+    def assumeAsList(self, strOrList):
+        """return a list as sys.argv if string
+        """
+        if type(strOrList) is list:
+          return list(strOrList) # copy
+        else:
+          return strOrList.split(" ") # supposed string to split for convenience
 
 
     def _getParser(self):
@@ -441,8 +440,16 @@ class Sat(object):
         parser.add_option('l', 'logs_paths_in_file', 'string', "logs_paths_in_file", 
                           _("put the command result and paths to log files."))
         return parser
-
-
+     
+    def parseGenericArguments(self, arguments):
+        args = self.assumeAsList(arguments)
+        genericOptions, remaindersArgs = self.parser.parse_args(args)
+        DBG.write("Sat generic arguments", genericArgs)
+        DBG.write("Sat remainders arguments", remaindersArgs)
+        return genericOptions, remaindersArgs
+               
+    
     def _getCommand(self, name):
         """
         create and add Command 'name' as instance of class in dict self.commands
@@ -476,15 +483,12 @@ class Sat(object):
             self.commands[name] = self._getCommand(name)
         return self.commands[name]    
        
-    def execute_command(self, opt=None):
+    def execute_cli(self, cli_arguments):
         """select first argument as a command in directory 'commands', and launch on arguments
         
-        :param opt str, optionnal: The sat options (as sys.argv)
+        :param args str or list, The sat cli arguments (as sys.argv)
         """
-        if opt is not None:
-            args = opt
-        else:
-            args = self.arguments 
+        args = self.assumeAsList(cli_arguments)
 
         # print general help and returns
         if len(args) == 0:
diff --git a/src/salomeTools_old.py b/src/salomeTools_old.py
deleted file mode 100755 (executable)
index 4036061..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-#  Copyright (C) 2010-2018  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-'''This file is the main entry file to salomeTools
-'''
-
-import os
-import sys
-import re
-import tempfile
-import imp
-import types
-import gettext
-import traceback
-import subprocess as SP
-
-#################################
-# NOT MAIN allowed
-#################################
-if __name__ == "__main__":
-    sys.stderr.write("\nERROR: 'salomeTools.py' is not main command entry for sat: use 'sat' instead.\n\n")
-    KOSYS = 1 # avoid import src
-    sys.exit(KOSYS)
-
-
-import src.debug as DBG # Easy print stderr (for DEBUG only)
-
-# get path to src
-rootdir = os.path.realpath( os.path.join(os.path.dirname(__file__), "..") )
-DBG.write("sat rootdir", rootdir)  
-srcdir = os.path.join(rootdir, "src")
-cmdsdir = os.path.join(rootdir, "commands")
-
-# load resources for internationalization
-gettext.install('salomeTools', os.path.join(srcdir, 'i18n'))
-
-# salomeTools imports
-import src
-import commands.config
-
-# The possible hooks : 
-# pre is for hooks to be executed before commands
-# post is for hooks to be executed after commands
-C_PRE_HOOK = "pre"
-C_POST_HOOK = "post"
-
-_LANG = os.environ["LANG"] # original locale
-
-def find_command_list(dirPath):
-    '''Parse files in dirPath that end with .py : it gives commands list
-    
-    :param dirPath str: The directory path where to search the commands
-    :return: cmd_list : the list containing the commands name 
-    :rtype: list
-    '''
-    cmd_list = []
-    for item in os.listdir(dirPath):
-        if item in ["__init__.py"]: #avoid theses files
-            continue
-        if item.endswith('.py'):
-            cmd_list.append(item[:-len('.py')])
-    return cmd_list
-
-# The list of valid salomeTools commands
-#lCommand = ['config', 'compile', 'prepare',...]
-lCommand = find_command_list(cmdsdir)
-
-def getCommandsList():
-    """Gives commands list (as basename of files commands/*.py)
-    """ 
-    return lCommand
-
-def launchSat(command):
-    """launch sat as subprocess popen
-    command as string ('sat --help' for example)
-    used for unittest, or else...
-    returns tuple (stdout, stderr)
-    """
-    if "sat" not in command.split()[0]:
-      raise Exception(_("Not a valid command for launchSat: '%s'") % command)
-    env = dict(os.environ)
-    env["PATH"] = rootdir + ":" + env["PATH"]
-    res =SP.Popen(command, shell=True, env=env, stdout=SP.PIPE, stderr=SP.PIPE).communicate()
-    return res
-
-def setNotLocale():
-    """force english at any moment"""
-    os.environ["LANG"] = ''
-    gettext.install('salomeTools', os.path.join(srcdir, 'i18n'))
-    DBG.write("setNotLocale", os.environ["LANG"])
-    
-def setLocale():
-    """reset initial locale at any moment 
-    'fr' or else 'TODO' from initial environment var '$LANG'
-    """
-    os.environ["LANG"] = _LANG
-    gettext.install('salomeTools', os.path.join(srcdir, 'i18n'))
-    DBG.write("setLocale", os.environ["LANG"])
-    
-# Define all possible option for salomeTools command :  sat <options> <args>
-parser = src.options.Options()
-parser.add_option('h', 'help', 'boolean', 'help', 
-                  _("shows global help or help on a specific command."))
-parser.add_option('o', 'overwrite', 'list', "overwrite", 
-                  _("overwrites a configuration parameters."))
-parser.add_option('g', 'debug', 'boolean', 'debug_mode', 
-                  _("run salomeTools in debug mode."))
-parser.add_option('v', 'verbose', 'int', "output_verbose_level", 
-                  _("change output verbose level (default is 3)."))
-parser.add_option('b', 'batch', 'boolean', "batch", 
-                  _("batch mode (no question)."))
-parser.add_option('t', 'all_in_terminal', 'boolean', "all_in_terminal", 
-                  _("all traces in the terminal (for example compilation logs)."))
-parser.add_option('l', 'logs_paths_in_file', 'string', "logs_paths_in_file", 
-                  _("put the command result and paths to log files."))
-    
-class Sat(object):
-    '''The main class that stores all the commands of salomeTools
-    '''
-    def __init__(self, opt='', datadir=None):
-        '''Initialization
-        
-        :param opt str or list: The sat options 
-        :param: datadir str : the directory that contain all the external 
-                              data (like software pyconf and software scripts)
-        '''
-        # Read the salomeTools prefixes options before the 'commands' tag
-        # sat <options> <args>
-        # (the list of possible options is  at the beginning of this file)                
-        try:
-            if type(opt) is not list: # as string 'sat --help' for example'
-                opts = opt.split()
-            else:
-                opts = opt
-            options, args = parser.parse_args(opts)
-            # DBG.write("Sat args", args)
-            # DBG.write("Sat options", options)    
-        except Exception as exc:
-            write_exception(exc)
-            sys.exit(src.KOSYS)
-
-        # initialization of class attributes       
-        self.__dict__ = dict()
-        self.cfg = None # the config that will be read using pyconf module
-        self.arguments = args # args are postfixes options: args[0] is the 'commands' command
-        self.options = options # the options passed to salomeTools
-        self.datadir = datadir # default value will be <salomeTools root>/data
-        # set the commands by calling the dedicated function
-        self._setCommands(cmdsdir)
-        
-        '''
-        # done with execute_command, to avoid sys.exit
-        # if the help option has been called, print help and exit
-        if options.help:
-            try:
-                self.print_help(args)
-                sys.exit(src.OKSYS)
-            except Exception as exc:
-                write_exception(exc)
-                DBG.write("args", args, True) 
-                sys.exit(src.KOSYS)
-        '''
-
-    def __getattr__(self, name):
-        '''overwrite of __getattr__ function in order to display 
-           a customized message in case of a wrong call
-        
-        :param name str: The name of the attribute 
-        '''
-        if name in self.__dict__:
-            return self.__dict__[name]
-        else:
-            raise AttributeError("'%s'" % name + _(" is not a valid command"))
-
-    def execute_command(self, opt=None):
-        """select first argument as a command in directory 'commands', and launch on arguments
-        
-        :param opt str, optionnal: The sat options (as sys.argv)
-        """
-        if opt is not None:
-            args = opt
-        else:
-            args = self.arguments 
-
-        # print general help and returns
-        if len(args) == 0:
-            print_help()
-            return src.OKSYS
-            
-        # if the help option has been called, print command help and returns
-        if self.options.help:
-            self.print_help(self.arguments)
-            return src.OKSYS
-       
-        # the command called
-        command = args[0]
-        # get dynamically the command function to call
-        fun_command = self.__getattr__(command)
-        # Run the command using the arguments
-        exitCode = fun_command(args[1:])
-        return exitCode 
-    
-    def _setCommands(self, dirPath):
-        '''set class attributes corresponding to all commands that are 
-           in the dirPath directory
-        
-        :param dirPath str: The directory path containing the commands 
-        '''
-        # loop on the commands name
-        for nameCmd in lCommand:
-            
-            # Exception for the jobs command that requires the paramiko module
-            if nameCmd == "jobs":
-                try:
-                    saveout = sys.stderr
-                    ff = tempfile.TemporaryFile()
-                    sys.stderr = ff
-                    import paramiko
-                    sys.stderr = saveout
-                except:
-                    sys.stderr = saveout
-                    continue
-
-            # load the module that has name nameCmd in dirPath
-            (file_, pathname, description) = imp.find_module(nameCmd, [dirPath])
-            module = imp.load_module(nameCmd, file_, pathname, description)
-            
-            def run_command(args='',
-                            options=None,
-                            batch = False,
-                            verbose = -1,
-                            logger_add_link = None):
-                '''The function that will load the configuration (all pyconf)
-                and return the function run of the command corresponding to module
-                
-                :param args str: The arguments of the command 
-                '''
-                # Make sure the internationalization is available
-                # gettext.install('salomeTools', os.path.join(srcdir, 'i18n'))
-
-                # Get the arguments in a list and remove the empty elements
-                if type(args) == type(''):
-                    # split by spaces without considering spaces in quotes
-                    argv_0 = re.findall(r'(?:"[^"]*"|[^\s"])+', args)
-                else:
-                    argv_0 = args
-                
-                if argv_0 != ['']:
-                    while "" in argv_0: argv_0.remove("")
-                
-                # Format the argv list in order to prevent strings 
-                # that contain a blank to be separated
-                argv = []
-                elem_old = ""
-                for elem in argv_0:
-                    if argv == [] or elem_old.startswith("-") or elem.startswith("-"):
-                        argv.append(elem)
-                    else:
-                        argv[-1] += " " + elem
-                    elem_old = elem
-                           
-                # if it is provided by the command line, get the application
-                appliToLoad = None
-                if argv not in [[''], []] and argv[0][0] != "-":
-                    appliToLoad = argv[0].rstrip('*')
-                    argv = argv[1:]
-                
-                # Check if the global options of salomeTools have to be changed
-                if options:
-                    options_save = self.options
-                    self.options = options  
-
-                # read the configuration from all the pyconf files    
-                cfgManager = commands.config.ConfigManager()
-                self.cfg = cfgManager.get_config(datadir=self.datadir, 
-                                                 application=appliToLoad, 
-                                                 options=self.options, 
-                                                 command=__nameCmd__)
-                               
-                # Set the verbose mode if called
-                if verbose > -1:
-                    verbose_save = self.options.output_verbose_level
-                    self.options.__setattr__("output_verbose_level", verbose)    
-
-                # Set batch mode if called
-                if batch:
-                    batch_save = self.options.batch
-                    self.options.__setattr__("batch", True)
-
-                # set output level
-                if self.options.output_verbose_level is not None:
-                    self.cfg.USER.output_verbose_level = self.options.output_verbose_level
-                if self.cfg.USER.output_verbose_level < 1:
-                    self.cfg.USER.output_verbose_level = 0
-                silent = (self.cfg.USER.output_verbose_level == 0)
-
-                # create log file
-                micro_command = False
-                if logger_add_link:
-                    micro_command = True
-                logger_command = src.logger.Logger(self.cfg, 
-                                   silent_sysstd=silent,
-                                   all_in_terminal=self.options.all_in_terminal,
-                                   micro_command=micro_command)
-                
-                # Check that the path given by the logs_paths_in_file option
-                # is a file path that can be written
-                if self.options.logs_paths_in_file and not micro_command:
-                    try:
-                        self.options.logs_paths_in_file = os.path.abspath(
-                                                self.options.logs_paths_in_file)
-                        dir_file = os.path.dirname(self.options.logs_paths_in_file)
-                        if not os.path.exists(dir_file):
-                            os.makedirs(dir_file)
-                        if os.path.exists(self.options.logs_paths_in_file):
-                            os.remove(self.options.logs_paths_in_file)
-                        file_test = open(self.options.logs_paths_in_file, "w")
-                        file_test.close()
-                    except Exception as e:
-                        msg = _("WARNING: the logs_paths_in_file option will "
-                                "not be taken into account.\nHere is the error:")
-                        logger_command.write("%s\n%s\n\n" % (
-                                             src.printcolors.printcWarning(msg),
-                                             str(e)))
-                        self.options.logs_paths_in_file = None
-                
-                options_launched = ""
-                res = None
-                try:
-                    # Execute the hooks (if there is any) 
-                    # and run method of the command
-                    self.run_hook(__nameCmd__, C_PRE_HOOK, logger_command)
-                    res = __module__.run(argv, self, logger_command)
-                    self.run_hook(__nameCmd__, C_POST_HOOK, logger_command)
-                    if res is None:
-                        res = 0
-                        
-                except Exception as e:
-                    # Get error
-                    logger_command.write("\n***** ", 1)
-                    logger_command.write(
-                        src.printcolors.printcError("salomeTools ERROR:"), 1)
-                    logger_command.write("\n" + str(e) + "\n\n", 1)
-                    # get stack
-                    __, __, exc_traceback = sys.exc_info()
-                    fp = tempfile.TemporaryFile()
-                    traceback.print_tb(exc_traceback, file=fp)
-                    fp.seek(0)
-                    stack = fp.read()
-                    verbosity = 5
-                    if self.options.debug_mode:
-                        verbosity = 1
-                    logger_command.write("TRACEBACK:\n%s" % stack.replace('"',"'"),
-                                         verbosity)
-                finally:
-                    # set res if it is not set in the command
-                    if res is None:
-                        res = 1
-                                            
-                    # come back to the original global options
-                    if options:
-                        options_launched = get_text_from_options(self.options)
-                        self.options = options_save
-                    
-                    # come back in the original batch mode if 
-                    # batch argument was called
-                    if batch:
-                        self.options.__setattr__("batch", batch_save)
-
-                    # come back in the original verbose mode if 
-                    # verbose argument was called                        
-                    if verbose > -1:
-                        self.options.__setattr__("output_verbose_level", 
-                                                 verbose_save)
-                    # put final attributes in xml log file 
-                    # (end time, total time, ...) and write it
-                    launchedCommand = ' '.join([self.cfg.VARS.salometoolsway +
-                                                os.path.sep +
-                                                'sat',
-                                                options_launched,
-                                                __nameCmd__, 
-                                                ' '.join(argv_0)])
-                    launchedCommand = launchedCommand.replace('"', "'")
-                    
-                    # Add a link to the parent command      
-                    if logger_add_link is not None:
-                        logger_add_link.add_link(logger_command.logFileName,
-                                                 __nameCmd__,
-                                                 res,
-                                                 launchedCommand)
-                        logger_add_link.l_logFiles += logger_command.l_logFiles
-                                            
-                    # Put the final attributes corresponding to end time and
-                    # Write the file to the hard drive
-                    logger_command.end_write(
-                                        {"launchedCommand" : launchedCommand})
-                    
-                    if res != 0:
-                        res = 1
-                        
-                    # print the log file path if 
-                    # the maximum verbose mode is invoked
-                    if not micro_command:
-                        logger_command.write("\nPath to the xml log file:\n", 5)
-                        logger_command.write("%s\n\n" % \
-                            src.printcolors.printcInfo(logger_command.logFilePath), 5)
-
-                    # If the logs_paths_in_file was called, write the result
-                    # and log files in the given file path
-                    if self.options.logs_paths_in_file and not micro_command:
-                        file_res = open(self.options.logs_paths_in_file, "w")
-                        file_res.write(str(res) + "\n")
-                        for i, filepath in enumerate(logger_command.l_logFiles):
-                            file_res.write(filepath)
-                            if i < len(logger_command.l_logFiles):
-                                file_res.write("\n")
-                                file_res.flush()
-                
-                return res
-
-            # Make sure that run_command will be redefined 
-            # at each iteration of the loop
-            globals_up = {}
-            globals_up.update(run_command.__globals__)
-            globals_up.update({'__nameCmd__': nameCmd, '__module__' : module})
-            func = types.FunctionType(run_command.__code__,
-                                      globals_up,
-                                      run_command.__name__,
-                                      run_command.__defaults__,
-                                      run_command.__closure__)
-
-            # set the attribute corresponding to the command
-            self.__setattr__(nameCmd, func)
-
-    def run_hook(self, cmd_name, hook_type, logger):
-        '''Execute a hook file for a given command regarding the fact 
-           it is pre or post
-        
-        :param cmd_name str: The the command on which execute the hook
-        :param hook_type str: pre or post
-        :param logger Logger: the logging instance to use for the prints
-        '''
-        # The hooks must be defined in the application pyconf
-        # So, if there is no application, do not do anything
-        if not src.config_has_application(self.cfg):
-            return
-
-        # The hooks must be defined in the application pyconf in the
-        # APPLICATION section, hook : { command : 'script_path.py'}
-        if "hook" not in self.cfg.APPLICATION \
-                    or cmd_name not in self.cfg.APPLICATION.hook:
-            return
-
-        # Get the hook_script path and verify that it exists
-        hook_script_path = self.cfg.APPLICATION.hook[cmd_name]
-        if not os.path.exists(hook_script_path):
-            raise src.SatException(_("Hook script not found: %s") % 
-                                   hook_script_path)
-        
-        # Try to execute the script, catch the exception if it fails
-        try:
-            # import the module (in the sense of python)
-            pymodule = imp.load_source(cmd_name, hook_script_path)
-            
-            # format a message to be printed at hook execution
-            msg = src.printcolors.printcWarning(_("Run hook script"))
-            msg = "%s: %s\n" % (msg, 
-                                src.printcolors.printcInfo(hook_script_path))
-            
-            # run the function run_pre_hook if this function is called 
-            # before the command, run_post_hook if it is called after
-            if hook_type == C_PRE_HOOK and "run_pre_hook" in dir(pymodule):
-                logger.write(msg, 1)
-                pymodule.run_pre_hook(self.cfg, logger)
-            elif hook_type == C_POST_HOOK and "run_post_hook" in dir(pymodule):
-                logger.write(msg, 1)
-                pymodule.run_post_hook(self.cfg, logger)
-
-        except Exception as exc:
-            msg = _("Unable to run hook script: %s") % hook_script_path
-            msg += "\n" + str(exc)
-            raise src.SatException(msg)
-
-    def print_help(self, opt):
-        '''Prints help for a command. Function called when "sat -h <command>"
-        
-        :param argv str: the options passed (to get the command name)
-        '''
-        # if no command as argument (sat -h)
-        if len(opt)==0:
-            print_help()
-            return
-            
-        # get command name
-        command = opt[0]
-        # read the configuration from all the pyconf files
-        cfgManager = commands.config.ConfigManager()
-        self.cfg = cfgManager.get_config(datadir=self.datadir)
-
-        # Check if this command exists
-        if not hasattr(self, command):
-            raise src.SatException(_("Command '%s' does not exist") % command)
-        
-        # load the module
-        module = self.get_module(command)
-        
-        msg = self.get_module_help(module)
-            
-        if isStdoutPipe():
-            # clean color if the terminal is redirected by user
-            # ex: sat compile appli > log.txt
-            msg = src.printcolors.cleancolor(msg)  
-        print(msg)
-        return 
-            
-    def get_module_help(self, module):
-        """get help for a command
-        as 'sat --help config' for example
-        """
-        # get salomeTools version
-        msg = get_version() + "\n\n"
-        
-        # print the description of the command that is done in the command file
-        if hasattr( module, "description" ):
-            msg += src.printcolors.printcHeader( _("Description:") ) + "\n"
-            msg += module.description() + "\n\n"
-
-        # print the description of the command options
-        if hasattr( module, "parser" ) :
-            msg += module.parser.get_help() + "\n"
-        return msg
-        
-
-    def get_module(self, module):
-        '''Loads a command. Function called only by print_help
-        
-        :param module str: the command to load
-        '''
-        # Check if this command exists
-        if not hasattr(self, module):
-            raise src.SatException(_("Command '%s' does not exist") % module)
-
-        # load the module
-        (file_, pathname, description) = imp.find_module(module, [cmdsdir])
-        module = imp.load_module(module, file_, pathname, description)
-        return module
-        
-
-def get_text_from_options(options):
-    text_options = ""
-    for attr in dir(options):
-        if attr.startswith("__"):
-            continue
-        if options.__getattr__(attr) != None:
-            option_contain = options.__getattr__(attr)
-            if type(option_contain)==type([]):
-                option_contain = ",".join(option_contain)
-            if type(option_contain)==type(True):
-                option_contain = ""
-            text_options+= "--%s %s " % (attr, option_contain)
-    return text_options
-         
-
-def isStdoutPipe():
-    """check if the terminal is redirected by user (elsewhere a tty) 
-    example: 
-    >> sat compile appli > log.txt
-    """
-    return not ('isatty' in dir(sys.stdout) and sys.stdout.isatty())
-     
-def get_version():
-    """get version colored string
-    """
-    cfgManager = commands.config.ConfigManager()
-    cfg = cfgManager.get_config()
-    # print the key corresponding to salomeTools version
-    msg = src.printcolors.printcHeader( _("Version: ") ) + \
-          cfg.INTERNAL.sat_version
-    return msg
-  
-def get_help():
-    """get general help colored string
-    """
-    # read the config 
-    msg = get_version() + "\n\n"
-    msg += src.printcolors.printcHeader(_("Usage: ")) + \
-          "sat [sat_options] <command> [product] [command_options]\n\n"
-    msg += parser.get_help() + "\n"
-    msg += src.printcolors.printcHeader(_("Available commands are:")) + "\n\n"
-    for command in lCommand:
-        msg += " - %s\n" % (command)
-    msg += "\n"
-    # Explain how to get the help for a specific command
-    msg += src.printcolors.printcHeader(
-           _("Getting the help for a specific command: ")) + \
-           "sat --help <command>\n"
-    return msg
-
-def print_help():
-    """prints salomeTools general help
-    """
-    msg = get_help() 
-    if isStdoutPipe():
-        # clean color if the terminal is redirected by user
-        # ex: sat compile appli > log.txt
-        msg = src.printcolors.cleancolor(msg)  
-    print(msg)
-    return
-
-def write_exception(exc):
-    '''write in stderr exception in case of error in a command
-    
-    :param exc exception: the exception to print
-    '''
-    sys.stderr.write("\n***** ")
-    sys.stderr.write(src.printcolors.printcError("salomeTools ERROR:"))
-    sys.stderr.write("\n" + str(exc) + "\n")
-
-
-
-
diff --git a/src/utilsSat.py b/src/utilsSat.py
new file mode 100644 (file)
index 0000000..07f3995
--- /dev/null
@@ -0,0 +1,398 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+#  Copyright (C) 2010-2018  CEA/DEN
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+"""
+utilities for sat
+general useful simple methods 
+"""
+
+import os
+import shutil
+import errno
+import stat
+
+
+
+def ensure_path_exists(p):
+    '''Create a path if not existing
+    
+    :param p str: The path.
+    '''
+    if not os.path.exists(p):
+        os.makedirs(p)
+        
+def check_config_has_application( config, details = None ):
+    '''check that the config has the key APPLICATION. Else raise an exception.
+    
+    :param config class 'common.pyconf.Config': The config.
+    '''
+    if 'APPLICATION' not in config:
+        message = _("An APPLICATION is required. Use 'config --list' to get"
+                    " the list of available applications.\n")
+        if details :
+            details.append(message)
+        raise SatException( message )
+
+def check_config_has_profile( config, details = None ):
+    '''check that the config has the key APPLICATION.profile.
+       Else, raise an exception.
+    
+    :param config class 'common.pyconf.Config': The config.
+    '''
+    check_config_has_application(config)
+    if 'profile' not in config.APPLICATION:
+        message = _("A profile section is required in your application.\n")
+        if details :
+            details.append(message)
+        raise SatException( message )
+
+def config_has_application( config ):
+    return 'APPLICATION' in config
+
+def get_cfg_param(config, param_name, default):
+    '''Search for param_name value in config.
+       If param_name is not in config, then return default,
+       else, return the found value
+       
+    :param config class 'common.pyconf.Config': The config.
+    :param param_name str: the name of the parameter to get the value
+    :param default str: The value to return if param_name is not in config
+    :return: see initial description of the function
+    :rtype: str
+    '''
+    if param_name in config:
+        return config[param_name]
+    return default
+
+def print_info(logger, info):
+    '''Prints the tuples that are in info variable in a formatted way.
+    
+    :param logger Logger: The logging instance to use for the prints.
+    :param info list: The list of tuples to display
+    '''
+    # find the maximum length of the first value of the tuples in info
+    smax = max(map(lambda l: len(l[0]), info))
+    # Print each item of info with good indentation
+    for i in info:
+        sp = " " * (smax - len(i[0]))
+        printcolors.print_value(logger, sp + i[0], i[1], 2)
+    logger.write("\n", 2)
+
+def get_base_path(config):
+    '''Returns the path of the products base.
+    
+    :param config Config: The global Config instance.
+    :return: The path of the products base.
+    :rtype: str
+    '''
+    if "base" not in config.LOCAL:
+        local_file_path = os.path.join(config.VARS.salometoolsway,
+                                      "data",
+                                      "local.pyconf")
+        msg = _("Please define a base path in the file %s") % local_file_path
+        raise SatException(msg)
+        
+    base_path = os.path.abspath(config.LOCAL.base)
+    
+    return base_path
+
+def get_launcher_name(config):
+    '''Returns the name of salome launcher.
+    
+    :param config Config: The global Config instance.
+    :return: The name of salome launcher.
+    :rtype: str
+    '''
+    check_config_has_application(config)
+    if 'profile' in config.APPLICATION and 'launcher_name' in config.APPLICATION.profile:
+        launcher_name = config.APPLICATION.profile.launcher_name
+    else:
+        launcher_name = 'salome'
+
+    return launcher_name
+
+def get_log_path(config):
+    '''Returns the path of the logs.
+    
+    :param config Config: The global Config instance.
+    :return: The path of the logs.
+    :rtype: str
+    '''
+    if "log_dir" not in config.LOCAL:
+        local_file_path = os.path.join(config.VARS.salometoolsway,
+                                      "data",
+                                      "local.pyconf")
+        msg = _("Please define a log_dir in the file %s") % local_file_path
+        raise SatException(msg)
+      
+    log_dir_path = os.path.abspath(config.LOCAL.log_dir)
+    
+    return log_dir_path
+
+def get_salome_version(config):
+    if hasattr(config.APPLICATION, 'version_salome'):
+        Version = config.APPLICATION.version_salome
+    else:
+        KERNEL_info = product.get_product_config(config, "KERNEL")
+        VERSION = os.path.join(
+                            KERNEL_info.install_dir,
+                            "bin",
+                            "salome",
+                            "VERSION")
+        if not os.path.isfile(VERSION):
+            return None
+            
+        fVERSION = open(VERSION)
+        Version = fVERSION.readline()
+        fVERSION.close()
+        
+    VersionSalome = int(only_numbers(Version))    
+    return VersionSalome
+
+def only_numbers(str_num):
+    return ''.join([nb for nb in str_num if nb in '0123456789'] or '0')
+
+def read_config_from_a_file(filePath):
+        try:
+            cfg_file = pyconf.Config(filePath)
+        except pyconf.ConfigError as e:
+            raise SatException(_("Error in configuration file: %(file)s\n  %(error)s") %
+                { 'file': filePath, 'error': str(e) } )
+        return cfg_file
+
+def get_tmp_filename(cfg, name):
+    if not os.path.exists(cfg.VARS.tmp_root):
+        os.makedirs(cfg.VARS.tmp_root)
+
+    return os.path.join(cfg.VARS.tmp_root, name)
+
+##
+# Utils class to simplify path manipulations.
+class Path:
+    def __init__(self, path):
+        self.path = str(path)
+
+    def __add__(self, other):
+        return Path(os.path.join(self.path, str(other)))
+
+    def __abs__(self):
+        return Path(os.path.abspath(self.path))
+
+    def __str__(self):
+        return self.path
+
+    def __eq__(self, other):
+        return self.path == other.path
+
+    def exists(self):
+        return self.islink() or os.path.exists(self.path)
+
+    def islink(self):
+        return os.path.islink(self.path)
+
+    def isdir(self):
+        return os.path.isdir(self.path)
+
+    def isfile(self):
+        return os.path.isfile(self.path)
+
+    def list(self):
+        return [Path(p) for p in os.listdir(self.path)]
+
+    def dir(self):
+        return Path(os.path.dirname(self.path))
+
+    def base(self):
+        return Path(os.path.basename(self.path))
+
+    def make(self, mode=None):
+        os.makedirs(self.path)        
+        if mode:
+            os.chmod(self.path, mode)
+        
+    def chmod(self, mode):
+        os.chmod(self.path, mode)
+
+    def rm(self):    
+        if self.islink():
+            os.remove(self.path)
+        else:
+            shutil.rmtree( self.path, onerror = handleRemoveReadonly )
+
+    def copy(self, path, smart=False):
+        if not isinstance(path, Path):
+            path = Path(path)
+
+        if os.path.islink(self.path):
+            return self.copylink(path)
+        elif os.path.isdir(self.path):
+            return self.copydir(path, smart)
+        else:
+            return self.copyfile(path)
+
+    def smartcopy(self, path):
+        return self.copy(path, True)
+
+    def readlink(self):
+        if self.islink():
+            return os.readlink(self.path)
+        else:
+            return False
+
+    def symlink(self, path):
+        try:
+            os.symlink(str(path), self.path)
+            return True
+        except:
+            return False
+
+    def copylink(self, path):
+        try:
+            os.symlink(os.readlink(self.path), str(path))
+            return True
+        except:
+            return False
+
+    def copydir(self, dst, smart=False):
+        try:
+            names = self.list()
+
+            if not dst.exists():
+                dst.make()
+
+            for name in names:
+                if name == dst:
+                    continue
+                if smart and (str(name) in [".git", "CVS", ".svn"]):
+                    continue
+                srcname = self + name
+                dstname = dst + name
+                srcname.copy(dstname, smart)
+            return True
+        except:
+            return False
+
+    def copyfile(self, path):
+        try:
+            shutil.copy2(self.path, str(path))
+            return True
+        except:
+            return False
+
+def find_file_in_lpath(file_name, lpath, additional_dir = ""):
+    """Find in all the directories in lpath list the file that has the same name
+       as file_name. If it is found, return the full path of the file, else,
+       return False. 
+       The additional_dir (optional) is the name of the directory to add to all 
+       paths in lpath.
+    
+    :param file_name str: The file name to search
+    :param lpath List: The list of directories where to search
+    :param additional_dir str: The name of the additional directory
+    :return: the full path of the file or False if not found
+    :rtype: str
+    """
+    for directory in lpath:
+        dir_complete = os.path.join(directory, additional_dir)
+        if not os.path.isdir(directory) or not os.path.isdir(dir_complete):
+            continue
+        l_files = os.listdir(dir_complete)
+        for file_n in l_files:
+            if file_n == file_name:
+                return os.path.join(dir_complete, file_name)
+    return False
+
+def handleRemoveReadonly(func, path, exc):
+    excvalue = exc[1]
+    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
+        os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
+        func(path)
+    else:
+        raise
+
+def deepcopy_list(input_list):
+    """ Do a deep copy of a list
+    
+    :param input_list List: The list to copy
+    :return: The copy of the list
+    :rtype: List
+    """
+    res = []
+    for elem in input_list:
+        res.append(elem)
+    return res
+
+def remove_item_from_list(input_list, item):
+    """ Remove all occurences of item from input_list
+    
+    :param input_list List: The list to modify
+    :return: The without any item
+    :rtype: List
+    """
+    res = []
+    for elem in input_list:
+        if elem == item:
+            continue
+        res.append(elem)
+    return res
+
+def parse_date(date):
+    """Transform YYYYMMDD_hhmmss into YYYY-MM-DD hh:mm:ss.
+    
+    :param date str: The date to transform
+    :return: The date in the new format
+    :rtype: str
+    """
+    if len(date) != 15:
+        return date
+    res = "%s-%s-%s %s:%s:%s" % (date[0:4],
+                                 date[4:6],
+                                 date[6:8],
+                                 date[9:11],
+                                 date[11:13],
+                                 date[13:])
+    return res
+
+def merge_dicts(*dict_args):
+    '''
+    Given any number of dicts, shallow copy and merge into a new dict,
+    precedence goes to key value pairs in latter dicts.
+    '''
+    result = {}
+    for dictionary in dict_args:
+        result.update(dictionary)
+    return result
+
+def replace_in_file(filein, strin, strout):
+    '''Replace <strin> by <strout> in file <filein>
+    '''
+    shutil.move(filein, filein + "_old")
+    fileout= filein
+    filein = filein + "_old"
+    fin = open(filein, "r")
+    fout = open(fileout, "w")
+    for line in fin:
+        fout.write(line.replace(strin, strout))
+
+def get_property_in_product_cfg(product_cfg, pprty):
+    if not "properties" in product_cfg:
+        return None
+    if not pprty in product_cfg.properties:
+        return None
+    return product_cfg.properties[pprty]