'''
# create a ConfigMerger to handle merge
- merger = src.pyconf.ConfigMerger()#MergeHandler())
+ merger = src.pyconf.ConfigMerger() #MergeHandler())
# create the configuration instance
cfg = src.pyconf.Config()
# 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__))
# 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)
+
-#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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()
--- /dev/null
+#!/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()
+
--- /dev/null
+#!/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
+
+++ /dev/null
-#!/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')
--- /dev/null
+#!/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
+++ /dev/null
-#!/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("}")
-
-
# 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
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__), "..") )
# _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
# 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,
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):
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
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:
+++ /dev/null
-#!/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")
-
-
-
-
--- /dev/null
+#!/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]