From e1f36d3ebfb86f289ea0f44542c43db1b752f883 Mon Sep 17 00:00:00 2001 From: Christian Van Wambeke Date: Fri, 19 Oct 2018 16:46:41 +0200 Subject: [PATCH] add src/versionMinorMajorPatch.py, not used --- commands/config.py | 118 +++++++++----- commands/log.py | 39 +++-- doc/src/conf.py | 4 +- src/__init__.py | 27 ++-- src/debug.py | 4 +- src/environment.py | 18 ++- src/fileEnviron.py | 42 +++-- src/logger.py | 31 +++- src/product.py | 2 + src/versionMinorMajorPatch.py | 296 ++++++++++++++++++++++++++++++++++ 10 files changed, 490 insertions(+), 91 deletions(-) create mode 100755 src/versionMinorMajorPatch.py diff --git a/commands/config.py b/commands/config.py index 4a216d2..6cb7f9d 100644 --- a/commands/config.py +++ b/commands/config.py @@ -43,7 +43,9 @@ parser.add_option('i', 'info', 'string', 'info', parser.add_option('l', 'list', 'boolean', 'list', _("Optional: list all available applications.")) parser.add_option('', 'show_patchs', 'boolean', 'show_patchs', - _("Optional: synthetic view of all patches used in the application")) + _("Optional: synthetic list of all patches used in the application")) +parser.add_option('', 'show_properties', 'boolean', 'show_properties', + _("Optional: synthetic list of all properties used in the application")) parser.add_option('c', 'copy', 'boolean', 'copy', _("""Optional: copy a config file to the personal config files directory. \tWARNING the included files are not copied. @@ -721,27 +723,53 @@ def show_product_info(config, name, logger): False) zz.set_python_libdirs() zz.set_a_product(name, logger) - + logger.write("\n", 2) + + def show_patchs(config, logger): - '''Prints all the used patchs in the application. - - :param config Config: the global configuration. - :param logger Logger: The logger instance to use for the display - ''' - len_max = max([len(p) for p in config.APPLICATION.products]) + 2 - for product in config.APPLICATION.products: - product_info = src.product.get_product_config(config, product) - if src.product.product_has_patches(product_info): - logger.write("%s: " % product, 1) - logger.write(src.printcolors.printcInfo( - " " * (len_max - len(product) -2) + - "%s\n" % product_info.patches[0]), - 1) - if len(product_info.patches) > 1: - for patch in product_info.patches[1:]: - logger.write(src.printcolors.printcInfo(len_max*" " + - "%s\n" % patch), 1) - logger.write("\n", 1) + '''Prints all the used patchs in the application. + + :param config Config: the global configuration. + :param logger Logger: The logger instance to use for the display + ''' + oneOrMore = False + for product in sorted(config.APPLICATION.products): + product_info = src.product.get_product_config(config, product) + if src.product.product_has_patches(product_info): + oneOrMore = True + logger.write("%s:\n" % product, 1) + for i in product_info.patches: + logger.write(src.printcolors.printcInfo(" %s\n" % i), 1) + if oneOrMore: + logger.write("\n", 1) + else: + logger.write("No patchs found\n", 1) + + +def show_properties(config, logger): + '''Prints all the used properties in the application. + + :param config Config: the global configuration. + :param logger Logger: The logger instance to use for the display + ''' + oneOrMore = False + for product in sorted(config.APPLICATION.products): + product_info = src.product.get_product_config(config, product) + done = False + try: + for i in product_info.properties: + if not done: + logger.write("%s:\n" % product, 1) + done = True + oneOrMore = True + logger.write(src.printcolors.printcInfo(" %s\n" % i), 1) + except: + # logger.write(src.printcolors.printcInfo(" %s\n" % "no properties"), 1) + pass + if oneOrMore: + logger.write("\n", 1) + else: + logger.write("No properties found\n", 1) def print_value(config, path, show_label, logger, level=0, show_full_path=False): '''Prints a value from the configuration. Prints recursively the values @@ -858,7 +886,7 @@ def run(args, runner, logger): if options.schema: get_config_children(runner.cfg, args) return - + # case : print a value of the config if options.value: if options.value == ".": @@ -882,7 +910,7 @@ def run(args, runner, logger): # case : edit user pyconf file or application file - elif options.edit: + if options.edit: editor = runner.cfg.USER.editor if ('APPLICATION' not in runner.cfg and 'open_application' not in runner.cfg): # edit user pyconf @@ -901,20 +929,19 @@ def run(args, runner, logger): break # case : give information about the product in parameter - elif options.info: + if options.info: src.check_config_has_application(runner.cfg) if options.info in runner.cfg.APPLICATION.products: show_product_info(runner.cfg, options.info, logger) - return - raise src.SatException(_("%(product_name)s is not a product " - "of %(application_name)s.") % - {'product_name' : options.info, - 'application_name' : - runner.cfg.VARS.application}) + # return + else: + msg = _("%s is not a product of %s.") % \ + (options.info, runner.cfg.VARS.application) + raise Exception(msg) # case : copy an existing .pyconf # to ~/.salomeTools/Applications/LOCAL_.pyconf - elif options.copy: + if options.copy: # product is required src.check_config_has_application( runner.cfg ) @@ -958,7 +985,7 @@ def run(args, runner, logger): logger.write(_("%s has been created.\n") % dest_file) # case : display all the available pyconf applications - elif options.list: + if options.list: lproduct = list() # search in all directories that can have pyconf applications for path in runner.cfg.PATHS.APPLICATIONPATH: @@ -985,18 +1012,27 @@ def run(args, runner, logger): logger.write("%s\n" % appliname) logger.write("\n") + + # case: print all the products name of the application (internal use for completion) + if options.completion: + for product_name in runner.cfg.APPLICATION.products.keys(): + logger.write("%s\n" % product_name) + # case : give a synthetic view of all patches used in the application - elif options.show_patchs: + if options.show_patchs: src.check_config_has_application(runner.cfg) # Print some informations - logger.write(_('Show the patchs of application %s\n') % + logger.write(_('Patchs of application %s\n') % src.printcolors.printcLabel(runner.cfg.VARS.application), 3) logger.write("\n", 2, False) show_patchs(runner.cfg, logger) - - # case: print all the products name of the application (internal use for completion) - elif options.completion: - for product_name in runner.cfg.APPLICATION.products.keys(): - logger.write("%s\n" % product_name) - - + + # case : give a synthetic view of all patches used in the application + if options.show_properties: + src.check_config_has_application(runner.cfg) + # Print some informations + logger.write(_('Properties of application %s\n') % + src.printcolors.printcLabel(runner.cfg.VARS.application), 3) + logger.write("\n", 2, False) + show_properties(runner.cfg, logger) + diff --git a/commands/log.py b/commands/log.py index d8def14..8d2aa1c 100644 --- a/commands/log.py +++ b/commands/log.py @@ -206,9 +206,9 @@ def run(args, runner, logger): # Parse the options (options, args) = parser.parse_args(args) - # get the log directory. + # get the log directory. logDir = src.get_log_path(runner.cfg) - + # Print a header nb_files_log_dir = len(glob.glob(os.path.join(logDir, "*"))) info = [("log directory", logDir), @@ -263,24 +263,29 @@ def run(args, runner, logger): # copy the stylesheets in the log directory # OP We use copy instead of copy2 to update the creation date # So we can clean the LOGS directories easily - shutil.copy(xslCommand, logDir) - shutil.copy(xslHat, logDir) - src.ensure_path_exists(os.path.join(logDir, "TEST")) - shutil.copy(xsltest, os.path.join(logDir, "TEST")) - shutil.copy(imgLogo, logDir) + try: + src.ensure_path_exists(logDir) + shutil.copy(xslCommand, logDir) + shutil.copy(xslHat, logDir) + src.ensure_path_exists(os.path.join(logDir, "TEST")) + shutil.copy(xsltest, os.path.join(logDir, "TEST")) + shutil.copy(imgLogo, logDir) + except: + # we are here if an user make sat log in jenkins LOGS without write rights + # Make a warning and do nothing + logger.warning("problem for writing in directory '%s', may be not owner." % logDir) # If the last option is invoked, just, show the last log file if options.last_terminal: src.check_config_has_application(runner.cfg) - log_dirs = os.listdir(os.path.join(runner.cfg.APPLICATION.workdir, - 'LOGS')) + log_dirs = os.listdir(os.path.join(runner.cfg.APPLICATION.workdir, 'LOGS')) show_last_logs(logger, runner.cfg, log_dirs) return 0 # If the last option is invoked, just, show the last log file if options.last: lastLogFilePath = get_last_log_file(logDir, - notShownCommands + ["config"]) + notShownCommands + ["config"]) if options.terminal: # Show the log corresponding to the selected command call print_log_command_in_terminal(lastLogFilePath, logger) @@ -328,14 +333,22 @@ def run(args, runner, logger): # Create or update the hat xml that gives access to all the commands log files logger.write(_("Generating the hat log file (can be long) ... "), 3) xmlHatFilePath = os.path.join(logDir, 'hat.xml') - src.logger.update_hat_xml(logDir, + try: + src.logger.update_hat_xml(logDir, application = runner.cfg.VARS.application, notShownCommands = notShownCommands) - logger.write(src.printcolors.printc("OK"), 3) + + logger.write(src.printcolors.printc("OK"), 3) + except: + logger.write(src.printcolors.printc("KO"), 3) + logger.write(" problem update hat.xml", 3) + logger.write("\n", 3) # open the hat xml in the user editor if not options.no_browser: - logger.write(_("\nOpening the log file\n"), 3) + logger.write(_("\nOpening the hat log file %s\n" % xmlHatFilePath), 3) src.system.show_in_editor(runner.cfg.USER.browser, xmlHatFilePath, logger) + else: + logger.write("\nHat log File is %s\n" % xmlHatFilePath, 3) return 0 diff --git a/doc/src/conf.py b/doc/src/conf.py index 3016b64..7888880 100644 --- a/doc/src/conf.py +++ b/doc/src/conf.py @@ -16,9 +16,9 @@ import os # Append source folder to path in order to enable autodoc currentPath = os.path.dirname(__file__) -print "sphinx on file", __file__ +print("sphinx on file %s" % __file__= dirAutodoc = os.path.realpath(os.path.join(currentPath, '..', '..')) -print "autodoc on dir", dirAutodoc +print("autodoc on dir %s" % dirAutodoc) sys.path.append(dirAutodoc) sys.path.append(dirAutodoc + "/commands") diff --git a/src/__init__.py b/src/__init__.py index c5535d5..91dfc66 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -180,27 +180,26 @@ def get_log_path(config): return log_dir_path def get_salome_version(config): + import versionMinorMajorPatch as VMMP + if hasattr(config.APPLICATION, 'version_salome'): - Version = config.APPLICATION.version_salome + version = VMMP.MinorMajorPatch(config.APPLICATION.version_salome) else: - KERNEL_info = product.get_product_config(config, "KERNEL") - VERSION = os.path.join( - KERNEL_info.install_dir, + kernel_info = product.get_product_config(config, "KERNEL") + aFile = os.path.join( + kernel_info.install_dir, "bin", "salome", "VERSION") - if not os.path.isfile(VERSION): + if not os.path.isfile(aFile): return None - - fVERSION = open(VERSION) - Version = fVERSION.readline() - fVERSION.close() - - VersionSalome = int(only_numbers(Version)) - return VersionSalome + with open(aFile) as f: + line = f.readline() # example: '[SALOME KERNEL] : 8.4.0' + version = VMMP.MinorMajorPatch(line.split(":")[1]) -def only_numbers(str_num): - return ''.join([nb for nb in str_num if nb in '0123456789'] or '0') + res = version.strCompact() + # print("get_salome_version %s -> %s" % (version, res)) + return res def read_config_from_a_file(filePath): try: diff --git a/src/debug.py b/src/debug.py index 13d7612..8c24d5d 100755 --- a/src/debug.py +++ b/src/debug.py @@ -234,7 +234,7 @@ def _saveConfigRecursiveDbg(config, aStream, indent, path, nb): indstr = indent * ' ' # '':no indent, ' ':indent strType = str(type(config)) - if debug: print "saveDbg Type", path, strType + if debug: print("saveDbg Type %s %s" % (path, strType)) if "Sequence" in strType: for i in range(len(config)): @@ -259,7 +259,7 @@ def _saveConfigRecursiveDbg(config, aStream, indent, path, nb): for key in sorted(data): #order): # data as sort alphabetical, order as initial order value = data[key] strType = str(type(value)) - if debug: print 'strType', path, key, strType + if debug: print('strType %s %s %s' % (path, key, strType)) if "Config" in strType: _saveConfigRecursiveDbg(value, aStream, indentp, path+"."+key, nbp) continue diff --git a/src/environment.py b/src/environment.py index 1c89d26..88a5959 100644 --- a/src/environment.py +++ b/src/environment.py @@ -25,6 +25,7 @@ import src import src.debug as DBG import pprint as PP + class Environ: """\ Class to manage the environment context @@ -65,12 +66,16 @@ class Environ: def append_value(self, key, value, sep=os.pathsep): """\ - append value to key using sep - + append value to key using sep, + if value contains ":" or ";" then raise error + :param key str: the environment variable to append :param value str: the value to append to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) # check if the key is already in the environment if key in self.environ: value_list = self.environ[key].split(sep) @@ -99,12 +104,17 @@ class Environ: def prepend_value(self, key, value, sep=os.pathsep): """\ - prepend value to key using sep + prepend value to key using sep, + if value contains ":" or ";" then raise error :param key str: the environment variable to prepend :param value str: the value to prepend to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("Environ prepend key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) + # check if the key is already in the environment if key in self.environ: value_list = self.environ[key].split(sep) if not value in value_list: @@ -124,7 +134,7 @@ class Environ: :param sep str: the separator string """ if isinstance(value, list): - for v in value: + for v in reversed(value): # prepend list, first item at last to stay first self.prepend_value(key, v, sep) else: self.prepend_value(key, value, sep) diff --git a/src/fileEnviron.py b/src/fileEnviron.py index dfcd1e5..2fafeaf 100644 --- a/src/fileEnviron.py +++ b/src/fileEnviron.py @@ -178,12 +178,16 @@ class FileEnviron(object): def append_value(self, key, value, sep=os.pathsep): """\ - append value to key using sep - + append value to key using sep, + if value contains ":" or ";" then raise error + :param key str: the environment variable to append :param value str: the value to append to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("FileEnviron append key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) self.set(key, self.get(key) + sep + value) if (key, sep) not in self.toclean: self.toclean.append((key, sep)) @@ -197,18 +201,23 @@ class FileEnviron(object): :param sep str: the separator string """ if isinstance(value, list): - self.append_value(key, sep.join(value), sep) + for v in value: + self.append_value(key, v, sep) else: self.append_value(key, value, sep) def prepend_value(self, key, value, sep=os.pathsep): """\ - prepend value to key using sep + prepend value to key using sep, + if value contains ":" or ";" then raise error :param key str: the environment variable to prepend :param value str: the value to prepend to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("FileEnviron prepend key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) self.set(key, value + sep + self.get(key)) if (key, sep) not in self.toclean: self.toclean.append((key, sep)) @@ -222,7 +231,8 @@ class FileEnviron(object): :param sep str: the separator string """ if isinstance(value, list): - self.prepend_value(key, sep.join(value), sep) + for v in reversed(value): # prepend list, first item at last to stay first + self.prepend_value(key, v, sep) else: self.prepend_value(key, value, sep) @@ -536,12 +546,16 @@ class LauncherFileEnviron: self.output.write('# "WARNING %s"\n' % warning) def append_value(self, key, value, sep=":"): - """append value to key using sep + """append value to key using sep, + if value contains ":" or ";" then raise error :param key str: the environment variable to append :param value str: the value to append to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("LauncherFileEnviron append key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) if self.is_defined(key) : self.add(key, value) else : @@ -555,17 +569,22 @@ class LauncherFileEnviron: :param sep str: the separator string """ if isinstance(value, list): - self.append_value(key, sep.join(value), sep) + for v in value: + self.append_value(key, v, sep) else: self.append_value(key, value, sep) def prepend_value(self, key, value, sep=":"): - """prepend value to key using sep + """prepend value to key using sep, + if value contains ":" or ";" then raise error :param key str: the environment variable to prepend :param value str: the value to prepend to key :param sep str: the separator string """ + for c in [";", ":"]: # windows or linux path separators + if c in value: + raise Exception("LauncherFileEnviron prepend key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) if self.is_defined(key) : self.add(key, value) else : @@ -579,7 +598,8 @@ class LauncherFileEnviron: :param sep str: the separator string """ if isinstance(value, list): - self.prepend_value(key, sep.join(value), sep) + for v in value: + self.prepend_value(key, v, sep) else: self.prepend_value(key, value, sep) @@ -764,8 +784,8 @@ def __initialize(): try: from salomeContextUtils import setOmniOrbUserPath setOmniOrbUserPath() - except Exception, e: - print e + except Exception as e: + print(e) sys.exit(1) # End of preliminary work diff --git a/src/logger.py b/src/logger.py index 94f81d6..49a8502 100755 --- a/src/logger.py +++ b/src/logger.py @@ -22,6 +22,7 @@ Implements the classes and method relative to the logging import sys import os +import stat import datetime import re import tempfile @@ -70,8 +71,22 @@ class Logger(object): # 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)) + + aDirLog = os.path.dirname(logFilePath) + if not os.path.exists(aDirLog): + print("create log dir %s" % aDirLog) + src.ensure_path_exists(aDirLog) + # sometimes other users make 'sat log' and create hat.xml file... + os.chmod(aDirLog, + stat.S_IRUSR | + stat.S_IRGRP | + stat.S_IROTH | + stat.S_IWUSR | + stat.S_IWGRP | + stat.S_IWOTH | + stat.S_IXUSR | + stat.S_IXGRP | + stat.S_IXOTH) src.ensure_path_exists(os.path.dirname(txtFilePath)) # The path of the log files (one for sat traces, and the other for @@ -450,8 +465,7 @@ def update_hat_xml(logDir, application=None, notShownCommands = []): """ # 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}) + 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) @@ -471,6 +485,15 @@ def update_hat_xml(logDir, application=None, notShownCommands = []): # Write the file on the hard drive xmlHat.write_tree('hat.xsl') + # Sometimes other users will make 'sat log' and update this file + os.chmod(xmlHatFilePath, + stat.S_IRUSR | + stat.S_IRGRP | + stat.S_IROTH | + stat.S_IWUSR | + stat.S_IWGRP | + stat.S_IWOTH ) + # TODO for future diff --git a/src/product.py b/src/product.py index 77fd3c6..47bd73a 100644 --- a/src/product.py +++ b/src/product.py @@ -346,6 +346,8 @@ def get_product_section(config, product_name, version, section=None): prod_info.section = section_range prod_info.from_file = config.PRODUCTS[product_name].from_file return prod_info + + # Else, get the standard informations if "default" in config.PRODUCTS[product_name]: diff --git a/src/versionMinorMajorPatch.py b/src/versionMinorMajorPatch.py new file mode 100755 index 0000000..d79831b --- /dev/null +++ b/src/versionMinorMajorPatch.py @@ -0,0 +1,296 @@ +#!/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 and utilities to define a version as MAJOR.MINOR.PATCH + +| Given a version number MAJOR.MINOR.PATCH separator "_" or "." +| increment the +| MAJOR version when you make incompatible API changes, +| MINOR version when you add functionality in a backwards-compatible manner, +| PATCH version when you make backwards-compatible bug fixes. +""" + +import os +import sys + +verbose = False # True + +############################################# +def only_numbers(aStr): + """ + Remove non numericals characters from string, + returns None if no numbers + """ + res = ''.join([nb for nb in aStr if nb in '0123456789']) + if res == "": + return None + else: + return res + +############################################# +def toList_majorMinorPatch(aStr): + """ + Returns list of integer as [major, minor, patch] from a string, + raise exception if problem + """ + if verbose: print("toList_majorMinorPatch('%s')" % aStr) + res = aStr.replace(" ", "").replace(".", "_").split("_") + if len(res) > 3: + msg = "Not a major_minor_patch correct syntax: '%s'" % aStr + raise Exception(msg) + if len(res) == 0: + msg = "An empty string is not a major_minor_patch syntax" + raise Exception(msg) + # complete MINOR.PATCH if not existing + if len(res) == 1: + res.append("0") + if len(res) == 2: + res.append("0") + + try: + ii = int(res[0]) + except: + msg = "major in major_minor_patch is not integer: '%s'" % aStr + raise Exception(msg) + if ii < 0: + msg = "major in major_minor_patch is negative integer: '%s'" % aStr + raise Exception(msg) + + try: + ii = int(res[1]) + except: + msg = "minor in major_minor_patch is not integer: '%s'" % aStr + raise Exception(msg) + if ii < 0: + msg = "minor in major_minor_patch is negative integer: '%s'" % aStr + raise Exception(msg) + + try: + ii = int(res[2]) + except: + msg = "patch in major_minor_patch is not integer: '%s'" % aStr + raise Exception(msg) + if ii < 0: + msg = "patch in major_minor_patch is negative integer: '%s'" % aStr + raise Exception(msg) + + return [int(i) for i in res] + +############################################# +def toCompactStr_majorMinorPatch(version): + """ + parameter version is list of integer as [major, minor, patch] + + | returns "789" for [7, 8, 9] + | warning: + | minor, pach have to be integer less than 10 + | raise exception for [7, 10, 11] + | (which returns "71011" as ambigous 710.1.1 for example) + """ + if len(version) != 3: + msg = "version major_minor_patch is incorrect: '%s'" % version + raise Exception(msg) + + aStr = '_'.join([str(i) for i in version]) + toList_majorMinorPatch(aStr) # will raise error if problem (as too much or negative values) + + res = "".join([str(i) for i in version]) + if version[1] > 9 or version[2] > 9: + raise Exception("ambigous major_minor_patch compact representation '%s' from '%s'" % (res, version)) + + return res + + +############################################# +class MinorMajorPatch(object): + """\ + class to define a version as MAJOR.MINOR.PATCH + + | Given a version number MAJOR.MINOR.PATCH separator "_" or "." + | increment the + | MAJOR version when you make incompatible API changes, + | MINOR version when you add functionality in a backwards-compatible manner, + | PATCH version when you make backwards-compatible bug fixes. + """ + + def __init__(self, version): + if type(version) == list: + aStr = '_'.join([str(i) for i in version]) + v = toList_majorMinorPatch(aStr) + else: + v = toList_majorMinorPatch(version) + self.major = v[0] + self.minor = v[1] + self.patch = v[2] + + def __repr__(self, sep="_"): + """example is 'version_1_2_3' """ + res = "version_%i%s%i%s%i" % (self.major, sep, self.minor, sep, self.patch) + return res + + def __str__(self, sep="."): + """example is '1.2.3' """ + res = "%i%s%i%s%i" % (self.major, sep, self.minor, sep, self.patch) + return res + + def strSalome(self): + """example is '1_2_3' """ + return self.__str__(sep="_") + + def strClassic(self): + """example is '1.2.3' """ + return self.__str__(sep=".") + + def strCompact(self): + """example is '123' from '1.2.3' """ + return toCompactStr_majorMinorPatch(self.toList()) + + def toList(self): + """example is list of integer [1, 2, 3] from '1.2.3' """ + return [self.major, self.minor, self.patch] + + def __lt__(self, other): + res = (self.toList() < other.toList()) + return res + + def __le__(self, other): + res = (self.toList() <= other.toList()) + return res + + def __gt__(self, other): + res = (self.toList() > other.toList()) + return res + + def __ge__(self, other): + res = (self.toList() >= other.toList()) + return res + + def __eq__(self, other): + res = (self.toList() == other.toList()) + return res + + def __ne__(self, other): + res = (self.toList() != other.toList()) + return res + +############################################# +import unittest +import pprint as PP + + +class TestCase(unittest.TestCase): + "Test the versionMajorMinorPatch.py""" + + def test_010(self): + if verbose: print(PP.pformat(dir(self))) + self.assertTrue(only_numbers("") is None) + self.assertEqual(only_numbers("1.2.3"), "123") + self.assertEqual(only_numbers("\n11.12.13\n"), "111213") + self.assertEqual(only_numbers(" \n 11.\t\n\t..12.13-rc2\n"), "1112132") + + def test_020(self): + res = [11, 222, 3333] + self.assertEqual(toList_majorMinorPatch("11.222.3333"), res) + self.assertEqual(toList_majorMinorPatch("11_222_3333"), res) + self.assertEqual(toList_majorMinorPatch("11.222_3333"), res) + self.assertEqual(toList_majorMinorPatch(" 11. 222 . 3333 "), res) + self.assertEqual(toList_majorMinorPatch("\n 11 . 222 . 3333 \n"), res) + self.assertEqual(toList_majorMinorPatch(" \n11.\t222.\r3333\n "), res) # could be tricky + + self.assertEqual(toList_majorMinorPatch("11"), [11, 0, 0]) + self.assertEqual(toList_majorMinorPatch("11.0"), [11, 0, 0]) + self.assertEqual(toList_majorMinorPatch("11.2"), [11, 2, 0]) + self.assertEqual(toList_majorMinorPatch("\n1 . 2 \n"), [1, 2, 0]) + + with self.assertRaises(Exception): toList_majorMinorPatch("") + with self.assertRaises(Exception): toList_majorMinorPatch("11.") + with self.assertRaises(Exception): toList_majorMinorPatch("11.2.") + with self.assertRaises(Exception): toList_majorMinorPatch("11.2.3.") + with self.assertRaises(Exception): toList_majorMinorPatch(".11") + with self.assertRaises(Exception): toList_majorMinorPatch("1_2_3_4") + with self.assertRaises(Exception): toList_majorMinorPatch("_1_2_3_") + with self.assertRaises(Exception): toList_majorMinorPatch(" \n 11...22.333-rc2\n") + with self.assertRaises(Exception): toList_majorMinorPatch(" \n 11...22.333-rc2\n") + with self.assertRaises(Exception): toList_majorMinorPatch(" \n 11...22.333-rc2\n") + + + def test_030(self): + self.assertEqual(toCompactStr_majorMinorPatch([1, 2, 3]), "123") + self.assertEqual(toCompactStr_majorMinorPatch([11, 2, 3]), "1123") + self.assertEqual(toCompactStr_majorMinorPatch([1, 9, 9]), "199") + + with self.assertRaises(Exception): toCompactStr_majorMinorPatch([1, 2, 10]) + with self.assertRaises(Exception): toCompactStr_majorMinorPatch([1, 10, 3]) + with self.assertRaises(Exception): toCompactStr_majorMinorPatch([10, 10, 10]) + + def test_040(self): + MMP = MinorMajorPatch + v = [1, 2, 3] + self.assertEqual(MMP(v).__str__(), "1.2.3") + self.assertEqual(MMP(v).__str__(sep="_"), "1_2_3") + self.assertEqual(str(MMP(v)), "1.2.3") + + self.assertEqual(MMP(v).__repr__(), "version_1_2_3") + self.assertEqual(MMP(v).__repr__(sep="."), "version_1.2.3") + + self.assertEqual(MMP(v).strSalome(), "1_2_3") + self.assertEqual(MMP(v).strClassic(), "1.2.3") + + self.assertEqual(MMP([' 123 \n', 2, 10]).strClassic(), "123.2.10") + self.assertEqual(MMP([' 123 \n', 2, 10]).strSalome(), "123_2_10") + self.assertEqual(MMP([' 123 \n', 2, 9]).strCompact(), "12329") # no ambigous + + with self.assertRaises(Exception): MMP([-5, 2, 10]) + with self.assertRaises(Exception): MMP([5, -2, 10]) + with self.assertRaises(Exception): MMP([5, 2, -10]) + with self.assertRaises(Exception): MMP(['-123', 2, 10]) + with self.assertRaises(Exception): MMP([123, 2, 10].strCompact()) # ambigous + + def test_040(self): + MMP = MinorMajorPatch + v000 = MMP("0.0.0") + v010 = MMP("0.1.0") + v100 = MMP("1.0.0") + v101 = MMP("1.0.1") + + va = v000 + vb = MMP("0.0.0") + self.assertTrue(va == vb) + self.assertTrue(va >= vb) + self.assertTrue(va <= vb) + self.assertFalse(va != vb) + self.assertFalse(va > vb) + self.assertFalse(va < vb) + + va = v000 + vb = v010 + self.assertFalse(va == vb) + self.assertFalse(va >= vb) + self.assertTrue(va <= vb) + self.assertTrue(va != vb) + self.assertFalse(va > vb) + self.assertTrue(va < vb) + + +if __name__ == '__main__': + unittest.main(exit=False) + pass + -- 2.30.2