From: Christian Van Wambeke Date: Thu, 17 May 2018 14:43:06 +0000 (+0200) Subject: fix logger and runner.getAnswer X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=3f40a13fc5779d37de25955579f28719c86360c0;p=tools%2Fsat.git fix logger and runner.getAnswer --- diff --git a/commands/clean.py b/commands/clean.py index 47f8f99..526840e 100644 --- a/commands/clean.py +++ b/commands/clean.py @@ -129,24 +129,19 @@ The '--properties' options must have the following syntax: options.sources_without_dev) if len(l_dir_to_suppress) == 0: - sat_command = ("sat -h clean") - msg = _("Nothing to suppress, Please specify what you want to suppress.") - logger.error(msg + "\nsee: '%s'\n" % sat_command) - return RCO.ReturnCode("KO", "specify what you want to suppress") + msg = _("Specify what you want to suppress in clean command.") + logger.error(msg + "\n(see: 'sat -h clean')") + return RCO.ReturnCode("KO", "to suppress in clean command") # Check with the user if he really wants to suppress the directories - if not runner.options.batch: - msg = _("Remove the following directories ?\n") - for directory in l_dir_to_suppress: - msg += " %s\n" % directory - logger.info(msg) - rep = input(_("Are you sure you want to continue? [Yes/No] ")) - if rep.upper() != _("YES"): - return RCO.ReturnCode("OK", "user do not want to continue") - + msg = UTS.red(_("Remove the following directories:\n")) + for directory in l_dir_to_suppress: + msg += " %s\n" % directory + if runner.getAnswer(msg[:-1]) == "No": + return RCO.ReturnCode("OK", "user do not want to continue") + # Suppress the list of paths - suppress_directories(l_dir_to_suppress, logger) - + suppress_directories(l_dir_to_suppress, logger) return RCO.ReturnCode("OK", "clean done") diff --git a/commands/find_duplicates.py b/commands/find_duplicates.py index 3ddeb03..2f3c586 100644 --- a/commands/find_duplicates.py +++ b/commands/find_duplicates.py @@ -26,10 +26,10 @@ from src.salomeTools import _BaseCommand default_extension_ignored = \ - 'html png txt js xml cmake gif m4 in pyo pyc doctree css'.split() + 'html png txt js xml cmake gif m4 in pyo pyc doctree css'.split() default_files_ignored = \ - '__init__.py Makefile.am VERSION build_configure README AUTHORS NEWS COPYING ChangeLog'.split() + '__init__.py Makefile.am VERSION build_configure README AUTHORS NEWS COPYING ChangeLog'.split() default_directories_ignored = [] diff --git a/commands/prepare.py b/commands/prepare.py index 9941c55..a73f417 100644 --- a/commands/prepare.py +++ b/commands/prepare.py @@ -82,7 +82,7 @@ class Command(_BaseCommand): products_infos = self.get_products_list(options, config) # Construct the arguments to pass to the clean, source and patch commands - args_appli = config.VARS.application + ' ' + args_appli = config.VARS.application args_product_opt = '--products ' if options.products: for p_name in options.products: @@ -120,9 +120,9 @@ Use the --force_patch option to overwrite it. logger) # Construct the final commands arguments - args_clean = args_appli + args_product_opt_clean + " --sources" - args_source = args_appli + args_product_opt - args_patch = args_appli + args_product_opt_patch + args_clean = "%s --sources" % (args_product_opt_clean) + args_source = "%s" % (args_product_opt) + args_patch = "%s" % (args_product_opt_patch) # If there is no more any product in the command arguments, # do not call the concerned command @@ -130,30 +130,34 @@ Use the --force_patch option to overwrite it. do_clean = not(oExpr.search(args_product_opt_clean)) do_source = not(oExpr.search(args_product_opt)) do_patch = not(oExpr.search(args_product_opt_patch)) - - + # Initialize the results to Ok but nothing done status res_clean = RCO.ReturnCode("OK", "nothing done") res_source = RCO.ReturnCode("OK", "nothing done") res_patch = RCO.ReturnCode("OK", "nothing done") - # return res_clean + res_source + res_patch - # Call the commands using the API if do_clean: msg = _("Clean the source directories ...") - logger.info(msg) - DBG.tofix("args_clean and TODO remove returns", args_clean, True) - res_clean = runner.getCommand("clean").run(args_clean) - return res_clean + res_source + res_patch + logger.info(msg + "(%s)" % args_clean) + mCmd = self.getMicroCommand("clean", args_appli) + res_clean = mCmd.run(args_clean) + logger.warning(str(res_clean)) + return res_clean # TODO debug remove that + if do_source: msg = _("Get the sources of the products ...") - logger.debug(msg) - res_source = runner.getCommand("source").run(args_source) + logger.info(msg + "(%s)" % args_source) + mCmd = self.getMicroCommand("source", args_appli) + res_source = mCmd.run(args_source) + logger.warning(str(res_source)) + if do_patch: msg = _("Patch the product sources (if any) ...") - logger.debug(msg) - res_patch = runner.getCommand("patch").run(args_patch) + logger.info(msg + "(%s)" % args_patch) + mCmd = self.getMicroCommand("patch", args_appli) + res_patch = mCmd.run(args_patch) + logger.warning(str(res_patch)) return res_clean + res_source + res_patch diff --git a/sat b/sat index f36b1cb..fec31e2 100755 --- a/sat +++ b/sat @@ -66,7 +66,7 @@ if __name__ == "__main__": except Exception as e: # error as may be unknown problem # verbose debug message with traceback if developers - msg = "Exception raised for execute_cli(%s):\n" % args + msg = "Exception raised for execute_cli (%s):\n" % " ".join(args) logger.critical(DBG.format_color_exception(msg)) logger.close() sys.exit(KOSYS) diff --git a/src/configManager.py b/src/configManager.py index bf8d26a..4cb62ee 100644 --- a/src/configManager.py +++ b/src/configManager.py @@ -162,7 +162,9 @@ class ConfigManager: return var def get_command_line_overrides(self, options, sections): - """get all the overwrites that are in the command line + """get all the overwrites that are in the command line. + When there are no options or not the overwrite option, + return an empty list :param options: The options from salomeTools class initialization @@ -170,8 +172,7 @@ class ConfigManager: :param sections: (str) The config section to overwrite. :return: (list) The list of all the overwrites to apply. """ - # when there are no options or not the overwrite option, - # return an empty list + # DBG.write("get_command_line_overrides options", options) if options is None or options.overwrite is None: return [] @@ -336,7 +337,7 @@ class ConfigManager: cp = cfg.PATHS.APPLICATIONPATH PYCONF.streamOpener = ConfigOpener(cp) do_merge = True - aFile = application + '.pyconf' + aFile = application.replace(" ", "") + '.pyconf' try: application_cfg = PYCONF.Config(aFile) except IOError as e: diff --git a/src/loggingSat.py b/src/loggingSat.py index 25220af..2cdc185 100755 --- a/src/loggingSat.py +++ b/src/loggingSat.py @@ -80,7 +80,7 @@ def indentUnittest(msg, prefix=" | "): def log(msg, force=False): """elementary log when no logging.Logger yet""" - prefix = "%s.log: " % _name + prefix = "---- %s.log: " % _name nb = len(prefix) if _verbose or force: print(prefix + indent(msg, nb)) @@ -114,6 +114,7 @@ def getStrHandler(handler): def getStrShort(msg): """Returns short string for msg (as first caracters without line feed""" + # log("getStrShort " + str(msg), True) res = msg.replace("\n", "//")[0:30] return res @@ -165,9 +166,9 @@ class LoggerSat(LOGI.Logger): """ if self.closed: raise Exception("logger closed yet: %s" % self) - log("close stuff logger %s" % self, True) # getStrDirLogger(self) + log("close stuff logger %s" % self) # getStrDirLogger(self) for handl in self.handlers: - log("close stuff handler %s" % getStrHandler(handl), True) + log("close stuff handler %s" % getStrHandler(handl)) handl.close() # Tidy up any resources used by the handler. # todo etc self.closed = True # done at end sat, flushed closed xml files. @@ -190,9 +191,9 @@ class LoggerSat(LOGI.Logger): | use the keyword argument exc_info with a true value | >> logger.trace("Houston, we have a %s", "long trace to follow") """ - log("trace stuff logger %s dateLogger %s", True) - if self.isEnabledFor(self._TRACE): - self._log(self._TRACE, msg, args, **kwargs) + log("trace stuff logger '%s' msg '%s...'" % (self.name, getStrShort(msg)), True) + if self.isEnabledFor(_TRACE): + self._log(_TRACE, msg, args, **kwargs) def xx_isEnabledFor(self, level): """ @@ -340,7 +341,7 @@ class XmlHandler(BufferingHandler): config = self._config # TODO for degug - log("XmlHandler to xml file\n%s" % PP.pformat(getListOfStrLogRecord(self.buffer)), True) + log("XmlHandler to xml file\n%s" % PP.pformat(getListOfStrLogRecord(self.buffer))) if os.path.exists(targetFile): msg = "XmlHandler target file %s existing yet" % targetFile @@ -403,7 +404,7 @@ def initLoggerAsUnittest(logger, fmt=None, level=None): logger.setLevel(logger.DEBUG) -def setFileHandler(logger, config): +def setFileHandler(cmdInstance): """ add file handler to logger to set log files for salometools command. @@ -418,10 +419,13 @@ def setFileHandler(logger, config): | ~/LOGS/OUT/micro_20180510_140607_clean_lenovo.txt | etc. """ + logger = cmdInstance.getLogger() + config = cmdInstance.getConfig() + #import src.debug as DBG # avoid cross import - log("setFileHandler %s" % logger, True) - log("setFileHandler config\n%s" % PP.pformat(dict(config.VARS)), True) - log("setFileHandler TODO set log_dir config.LOCAL.log_dir", True) + log("setFileHandler %s" % logger) + log("setFileHandler config\n%s" % PP.pformat(dict(config.VARS))) + log("setFileHandler TODO set log_dir config.LOCAL.log_dir") log_dir = "TMP" # TODO for debug config.LOCAL.log_dir # files xml log_dir_out = os.path.join(log_dir, "OUT") # files txt @@ -429,14 +433,22 @@ def setFileHandler(logger, config): UTS.ensure_path_exists(log_dir_out) datehour = config.VARS.datehour cmd = config.VARS.command + fullNameCmd = cmdInstance.getFullNameStr() hostname = config.VARS.hostname nameFileXml = "%s_%s_%s.xml" % (datehour, cmd, hostname) nameFileTxt = "%s_%s_%s.txt" % (datehour, cmd, hostname) fileXml = os.path.join(log_dir, nameFileXml) fileTxt = os.path.join(log_dir_out, nameFileTxt) + # precaution + lastCmd = cmdInstance.getFullNameList()[-1] + if cmd != lastCmd: + msg = "setFileHandler '%s' command name incoherency in config '%s'" % (fullNameCmd, cmd) + logger.critical(msg) + nbhandl = len(logger.handlers) # number of current handlers if nbhandl == 1: # first main command + log("setFileHandler '%s' main command" % fullNameCmd, True) # Logging vers file xml handler = XmlHandler(1000) # log outputs in memory @@ -462,10 +474,10 @@ def setFileHandler(logger, config): handler.setFormatter(formatter) logger.addHandler(handler) - if nbhandl > 1: # secondary micro command - log("setFileHandler micro command %s" % config.VARS.command, True) + elif nbhandl > 1: # secondary micro command + log("TODO setFileHandler '%s' micro command" % fullNameCmd, True) - log("setFileHandler %s" % logger, True) + log("setFileHandler %s" % logger) def getDefaultLogger(): diff --git a/src/salomeTools.py b/src/salomeTools.py index 795a1dc..0be634d 100755 --- a/src/salomeTools.py +++ b/src/salomeTools.py @@ -159,7 +159,7 @@ class _BaseCommand(object): """ # supposed never seen, set "config", "prepare"... in inherited commands - name = "salomeTools" + name = "NoName" def __init__(self, runner): # runner (as caller) usually as Sat instance @@ -169,6 +169,38 @@ class _BaseCommand(object): # logger (from caller) usually as Sat instance logger, but sometimes local value self._logger = runner.logger self._options = None + self._fullName = [] # example '[prepare','clean'] when micro command 'clean' of 'prepare' + + def initFullName(self, parentFullName=[]): + """ + initialize full name of command instance, one call only. + parentFullName supposedly as list (as parent full name) + + | example values of self._full name: + | ['prepare'] if main command 'prepare'. + | ['prepare', 'clean'] if micro command 'clean' from command 'prepare'. + """ + # DBG.write("initFullName", (self._fullName, parentFullName)) + if len(self._fullName) != 0: # ne call only + raise Exception("full name initialized yet '%s'" % self.getFullNameStr()) + if len(self._fullName) > 10: # Houston problem + raise Exception("full name too long '%s'" % self.getFullNameStr()) + if type(parentFullName) == str: + pfn = parentFullName.split("_") # if from string 'prepare_clean' + elif type(parentFullName) == list: + pfn = list(parentFullName) # make copy precaution + else: + raise Exception("type parent full name unexpected '%s'" % parentFullName) + self._fullName = pfn + [self.name] # make copy precaution + DBG.write("initFullName", self._fullName) + + def getFullNameStr(self): + """returns 'prepare_clean' when micro command 'clean' of 'prepare'""" + return "_".join(self._fullName) + + def getFullNameList(self): + """returns precaution copy as list of self._fullName""" + return list(self._fullName) def getClassName(self): """ @@ -182,6 +214,33 @@ class _BaseCommand(object): tmp = PP.pformat(self.__dict__) res = "%s(\n %s)\n" % (self.getClassName(), tmp[1:-1]) return res + + def getMicroCommand(self, nameCommandToLoad, nameAppliToLoad): + """ + get micro command instance from current command instance + returns inherited instance of Command(_BaseCmd) for command 'name' + if module not loaded yet, load it. + """ + # create/get dynamically the command instance to call its 'run' method + runner = self.getRunner() + options = runner.getOptions() # generic main options + + # load micro command config + cfgMgr = CFGMGR.ConfigManager(self) + DBG.write("getMicroCommand nameCommandToLoad '%s'" % nameCommandToLoad, nameAppliToLoad) + config = cfgMgr.get_config(nameAppliToLoad, options, nameCommandToLoad, datadir=None) + + cmdInstance = runner.getCommand(nameCommandToLoad) + + # some initialisation stuff + cmdInstance.initFullName(self.getFullNameList()) # as micro command + cmdInstance.setConfig(config) # micro command config + cmdInstance.setOptions(options) + + import src.loggingSat as LOG # avoid cross import + LOG.setFileHandler(cmdInstance) + + return cmdInstance def run(self, cmd_arguments): """ @@ -194,18 +253,24 @@ class _BaseCommand(object): """set logger for run command""" if self._logger is not None: # supposed logger.debug for future - self._logger.warning("change logger for %s, are you sure" % self.getClassName()) + self._logger.warning("change logger for %s, are you sure" % self.getFullNameStr()) self._logger = logger def getLogger(self): if self._logger is None: - raise Exception("%s instance needs self._logger not None, fix it." % self.getClassName()) + raise Exception("%s instance needs self._logger not None, fix it." % self.getFullNameStr()) else: return self._logger + def setOptions(self, options): + if self._options is None: + self._options = options + else: + raise Exception("%s command instance have options yet, Fix it." % self.getFullName()) + def getOptions(self): if self._options is None: - raise Exception("%s instance needs self._option not None, fix it." % self.getClassName()) + raise Exception("%s instance needs self._option not None, fix it." % self.getFullNameStr()) else: return self._options @@ -217,19 +282,34 @@ class _BaseCommand(object): def getRunner(self): if self._runner is None: - raise Exception("%s instance needs self.runner not None, fix it." % self.getClassName()) + raise Exception("%s instance needs self.runner not None, fix it." % self.getFullNameStr()) else: return self._runner - def getConfig(self): + def getConfigObsolete(self): """ supposedly (for now) no multiply local config(s) only one config in runner.config may be some for future... """ if self._runner.config is None: - self._logger.error("%s instance have runner.config None, fix it." % self.getClassName()) + self._logger.error("%s command instance have runner.config None, Fix it." % self.getFullNameStr()) return self._runner.config + + def getConfig(self): + """ + supposedly multiply local config(s) + only one config for each command instance + """ + if self._config is None: + raise Exception("%s command instance have config None, Fix it." % self.getFullNameStr()) + return self._config + + def setConfig(self, config): + if self._config is None: + self._config = config + else: + raise Exception("%s command instance have config yet, Fix it." % self.getFullName()) def get_products_list(self, options, config): return CFGMGR.get_products_list(options, config) @@ -245,14 +325,16 @@ class _BaseCommand(object): smart parse command arguments skip first argument name appli to load, if present """ + verb = False argList = self.assumeAsList(cmd_arguments) - DBG.write("%s.Command arguments" % self.name, argList) + fullName = self.getFullNameStr() + DBG.write("%s.Command arguments" % fullName, argList, verb) commandOptions, remaindersArgs = self.getParser().parse_args(argList) - DBG.write("%s.Command options" % self.name, commandOptions) - DBG.write("%s.Command remainders arguments" % self.name, remaindersArgs) + DBG.write("%s.Command options" % fullName, commandOptions, verb) + DBG.write("%s.Command remainders arguments" % fullName, remaindersArgs, verb) if remaindersArgs != []: - self.getLogger().error("%s.Command have unknown remainders arguments:\n%s\n" % \ - (self.name, remaindersArgs)) + msg = "%s.Command have unknown remainders arguments:\n(%s)" % (fullName, " ".join(remaindersArgs)) + self.getLogger().error(msg) return commandOptions, remaindersArgs def description(self): @@ -292,6 +374,7 @@ class _BaseCommand(object): # description of the command options msg += self.getParser().get_help() + "\n" return msg + ######################################################################## # Sat class @@ -315,7 +398,7 @@ class Sat(object): self.config = None # the config that will be read using pyconf module self.logger = logger # the logger that will be use self.arguments = None # args are postfixes options: args[0] is the 'commands' command - self.options = None # the options passed to salomeTools + self.options = None # the main generic options passed to salomeTools # the directory that contain all the external # data (like software pyconf and software scripts) @@ -354,6 +437,9 @@ class Sat(object): def getConfig(self): return self.config + def getOptions(self): + return self.options + def assumeAsList(self, strOrList): return assumeAsList(strOrList) @@ -444,28 +530,35 @@ class Sat(object): # if the help option has been called, print command help and returns if self.options.help: self.print_help() - return RCO.ReturnCode("OK", "Option --help") + return RCO.ReturnCode("OK", "Option --help") # and returns self.nameCommandToLoad, self.nameAppliToLoad, self.commandArguments = \ self.getCommandAndAppli(remainderArgs) - cfgManager = CFGMGR.ConfigManager(self) - self.config = cfgManager.get_config( - application=self.nameAppliToLoad, - options=self.options, - command=self.nameCommandToLoad, - datadir=None) + cfgMgr = CFGMGR.ConfigManager(self) + # as main config + config = cfgMgr.get_config(self.nameAppliToLoad, self.options, self.nameCommandToLoad, datadir=None) + self.config = config # runner.config main config # create/get dynamically the command instance to call its 'run' method cmdInstance = self.getCommand(self.nameCommandToLoad) + + # some initialisation stuff + cmdInstance.initFullName() # as main command + cmdInstance.setConfig(config) import src.loggingSat as LOG # avoid cross import - LOG.setFileHandler(self.getLogger(), self.getConfig()) + LOG.setFileHandler(cmdInstance) - # Run the command using the arguments + # Run the main command using the remainders command arguments returnCode = cmdInstance.run(self.commandArguments) + return returnCode def getCommandAndAppli(self, arguments): + """ + returns name command to load and name appli to load + and command to load remainders arguments + """ args = self.assumeAsList(arguments) namecmd, nameAppli, remainderArgs = None, None, [] iremain = 0 @@ -480,8 +573,7 @@ class Sat(object): remainderArgs = args[iremain:] res = (namecmd, nameAppli, remainderArgs) DBG.write("getCommandAndAppli", res) - return res - + return res def get_help(self): """get general help colored string""" @@ -515,6 +607,29 @@ class Sat(object): msg = "
Version: " + version return msg + def getConfirmMode(self): + return False + + def getBatchMode(self): + return True + + def getAnswer(self, msg): + """ + question and user answer (in console) if confirm mode and not batch mode + returns 'YES' or 'NO' if confirm mode and not batch mode + returns 'YES' if batch mode + """ + if self.getConfirmMode() and not self.getBatchMode(): + self.getLogger().info(msg) + rep = input(_("Are you sure you want to continue? [yes/no]")) + if rep.upper() == _("YES"): + return "YES" + else: + return "NO" + else: + self.getLogger().info(msg) + self.getLogger().info("YES (as automatic answer)") + return "YES" diff --git a/src/xsl/LOGO-SAT.png b/src/xsl/LOGO-SAT.png index eb27403..34f4729 100644 Binary files a/src/xsl/LOGO-SAT.png and b/src/xsl/LOGO-SAT.png differ diff --git a/src/xsl/LOGO-SAT_origin.png b/src/xsl/LOGO-SAT_origin.png new file mode 100644 index 0000000..ff019cb Binary files /dev/null and b/src/xsl/LOGO-SAT_origin.png differ diff --git a/test/test_024_logging.py b/test/test_024_logging.py index 3102386..4f37c60 100755 --- a/test/test_024_logging.py +++ b/test/test_024_logging.py @@ -57,6 +57,8 @@ import src.debug as DBG verbose = False #True +_TRACE = LOGI.INFO - 2 # just below info + class LoggerSat(LOGI.Logger): """ Elementary prototype for logger sat @@ -68,15 +70,13 @@ class LoggerSat(LOGI.Logger): see: /usr/lib64/python2.7/logging/xxx__init__.py etc. """ - _TRACE = LOGI.INFO - 2 # just below - def __init__(self, name, level=LOGI.INFO): """ Initialize the logger with a name and an optional level. """ super(LoggerSat, self).__init__(name, level) - LOGI.addLevelName(self._TRACE, "TRACE") - # LOGI.TRACE = self._TRACE # only for coherency, + LOGI.addLevelName(_TRACE, "TRACE") + # LOGI.TRACE = _TRACE # only for coherency, def trace(self, msg, *args, **kwargs): """ @@ -87,8 +87,8 @@ class LoggerSat(LOGI.Logger): logger.trace("Houston, we have a %s", "long trace to follow") """ - if self.isEnabledFor(self._TRACE): - self._log(self._TRACE, msg, args, **kwargs) + if self.isEnabledFor(_TRACE): + self._log(_TRACE, msg, args, **kwargs) class TestCase(unittest.TestCase): "Test the debug.py"""