]> SALOME platform Git repositories - tools/sat.git/commitdiff
Salome HOME
RE-organization of directories, according to CEA request
authorSerge Rehbinder <serge.rehbinder@cea.fr>
Thu, 4 Feb 2016 11:04:32 +0000 (12:04 +0100)
committerSerge Rehbinder <serge.rehbinder@cea.fr>
Thu, 4 Feb 2016 11:04:32 +0000 (12:04 +0100)
39 files changed:
commands/config.py [new file with mode: 0644]
commands/test_command.py [new file with mode: 0644]
salomeTools.py [new file with mode: 0755]
sat
src/__init__.py
src/architecture.py [new file with mode: 0644]
src/common/__init__.py [deleted file]
src/common/architecture.py [deleted file]
src/common/i18n/build_strings.sh [deleted file]
src/common/i18n/fr/LC_MESSAGES/salomeTools.mo [deleted file]
src/common/i18n/fr/LC_MESSAGES/salomeTools.po [deleted file]
src/common/i18n/translate.py [deleted file]
src/common/i18n/translate.sh [deleted file]
src/common/internal_config/distrib.pyconf [deleted file]
src/common/internal_config/salomeTools.pyconf [deleted file]
src/common/options.py [deleted file]
src/common/printcolors.py [deleted file]
src/common/pyconf.py [deleted file]
src/common/system.py [deleted file]
src/config.py [deleted file]
src/i18n/build_strings.sh [new file with mode: 0755]
src/i18n/fr/LC_MESSAGES/salomeTools.mo [new file with mode: 0644]
src/i18n/fr/LC_MESSAGES/salomeTools.po [new file with mode: 0644]
src/i18n/translate.py [new file with mode: 0644]
src/i18n/translate.sh [new file with mode: 0755]
src/internal_config/distrib.pyconf [new file with mode: 0644]
src/internal_config/salomeTools.pyconf [new file with mode: 0644]
src/options.py [new file with mode: 0644]
src/printcolors.py [new file with mode: 0644]
src/pyconf.py [new file with mode: 0644]
src/salomeTools.py [deleted file]
src/system.py [new file with mode: 0644]
src/test_command.py [deleted file]
test/config/create_user_pyconf.py
test/config/option_copy.py
test/config/option_value.py
test/config/option_value_2.py
test/run_all.sh
test/test.py

diff --git a/commands/config.py b/commands/config.py
new file mode 100644 (file)
index 0000000..cb9a84a
--- /dev/null
@@ -0,0 +1,492 @@
+#!/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
+
+import os
+import sys
+import platform
+import datetime
+import shutil
+import gettext
+
+import src
+
+# internationalization
+satdir  = os.path.dirname(os.path.realpath(__file__))
+gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
+
+# Define all possible option for config command :  sat config <options>
+parser = src.options.Options()
+parser.add_option('v', 'value', 'string', 'value', _("print the value of CONFIG_VARIABLE."))
+parser.add_option('e', 'edit', 'boolean', 'edit', _("edit the product configuration file."))
+parser.add_option('l', 'list', 'boolean', 'list',_("list all available applications."))
+parser.add_option('c', 'copy', 'boolean', 'copy',
+    _("""copy a config file to the personnal config files directory.
+\tWARNING the included files are not copied.
+\tIf a name is given the new config file takes the given name."""))
+
+class ConfigOpener:
+    ''' Class that helps to find an application pyconf in all the possible directories (pathList)
+    '''
+    def __init__(self, pathList):
+        '''Initialization
+        
+        :param pathList list: The list of paths where to serach a pyconf.
+        '''
+        self.pathList = pathList
+
+    def __call__(self, name):
+        if os.path.isabs(name):
+            return src.pyconf.ConfigInputStream(open(name, 'rb'))
+        else:
+            return src.pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') )
+        raise IOError(_("Configuration file '%s' not found") % name)
+
+    def getPath( self, name ):
+        '''The method that returns the entire path of the pyconf searched
+        :param name str: The name of the searched pyconf.
+        '''
+        for path in self.pathList:
+            if os.path.exists(os.path.join(path, name)):
+                return path
+        raise IOError(_("Configuration file '%s' not found") % name)
+
+class ConfigManager:
+    '''Class that manages the read of all the configuration files of salomeTools
+    '''
+    def __init__(self, dataDir=None):
+        pass
+
+    def _create_vars(self, application=None, command=None, dataDir=None):
+        '''Create a dictionary that stores all information about machine, user, date, repositories, etc...
+        
+        :param application str: The application for which salomeTools is called.
+        :param command str: The command that is called.
+        :param dataDir str: The repository that contain external data for salomeTools.
+        :return: The dictionary that stores all information.
+        :rtype: dict
+        '''
+        var = {}      
+        var['user'] = src.architecture.get_user()
+        var['salometoolsway'] = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        var['srcDir'] = os.path.join(var['salometoolsway'], 'src')
+        var['sep']= os.path.sep
+        
+        # dataDir has a default location
+        var['dataDir'] = os.path.join(var['salometoolsway'], 'data')
+        if dataDir is not None:
+            var['dataDir'] = dataDir
+
+        var['personalDir'] = os.path.join(os.path.expanduser('~'), '.salomeTools')
+
+        # read linux distributions dictionary
+        distrib_cfg = src.pyconf.Config(os.path.join(var['srcDir'], 'internal_config', 'distrib.pyconf'))
+        
+        # set platform parameters
+        dist_name = src.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS)
+        dist_version = src.architecture.get_distrib_version(dist_name, codes=distrib_cfg.VERSIONS)
+        dist = dist_name + dist_version
+        
+        var['dist_name'] = dist_name
+        var['dist_version'] = dist_version
+        var['dist'] = dist
+        var['python'] = src.architecture.get_python_version()
+
+        var['nb_proc'] = src.architecture.get_nb_proc()
+        node_name = platform.node()
+        var['node'] = node_name
+        var['hostname'] = node_name
+
+        # set date parameters
+        dt = datetime.datetime.now()
+        var['date'] = dt.strftime('%Y%m%d')
+        var['datehour'] = dt.strftime('%Y%m%d_%H%M%S')
+        var['hour'] = dt.strftime('%H%M%S')
+
+        var['command'] = str(command)
+        var['application'] = str(application)
+
+        # Root dir for temporary files 
+        var['tmp_root'] = os.sep + 'tmp' + os.sep + var['user']
+        # particular win case 
+        if src.architecture.is_windows() : 
+            var['tmp_root'] =  os.path.expanduser('~') + os.sep + 'tmp'
+        
+        return var
+
+    def get_command_line_overrides(self, options, sections):
+        '''get all the overwrites that are in the command line
+        
+        :param options: the options from salomeTools class initialization (like -l5 or --overwrite)
+        :param sections str: The config section to overwrite.
+        :return: The list of all the overwrites to apply.
+        :rtype: list
+        '''
+        # when there are no options or not the overwrite option, return an empty list
+        if options is None or options.overwrite is None:
+            return []
+        
+        over = []
+        for section in sections:
+            # only overwrite the sections that correspond to the option 
+            over.extend(filter(lambda l: l.startswith(section + "."), options.overwrite))
+        return over
+
+    def getConfig(self, application=None, options=None, command=None, dataDir=None):
+        '''get the config from all the configuration files.
+        
+        :param application str: The application for which salomeTools is called.
+        :param options calss Options: The general salomeToosl options (--overwrite or -l5, for example)
+        :param command str: The command that is called.
+        :param dataDir str: The repository that contain external data for salomeTools.
+        :return: The final config.
+        :rtype: class 'src.pyconf.Config'
+        '''        
+        
+        # create a ConfigMerger to handle merge
+        merger = src.pyconf.ConfigMerger()#MergeHandler())
+        
+        # create the configuration instance
+        cfg = src.pyconf.Config()
+        
+        # =======================================================================================
+        # create VARS section
+        var = self._create_vars(application=application, command=command, dataDir=dataDir)
+        # add VARS to config
+        cfg.VARS = src.pyconf.Mapping(cfg)
+        for variable in var:
+            cfg.VARS[variable] = var[variable]
+        
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["VARS"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+        
+        # =======================================================================================
+        # Load INTERNAL config
+        # read src/internal_config/salomeTools.pyconf
+        src.pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'internal_config')])
+        try:
+            internal_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'internal_config', 'salomeTools.pyconf')))
+        except src.pyconf.ConfigError as e:
+            raise src.SatException(_("Error in configuration file: salomeTools.pyconf\n  %(error)s") % \
+                {'error': str(e) })
+        
+        merger.merge(cfg, internal_cfg)
+
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec        
+        
+        # =======================================================================================
+        # Load SITE config file
+        # search only in the data directory
+        src.pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir])
+        try:
+            site_cfg = src.pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf')))
+        except src.pyconf.ConfigError as e:
+            raise src.SatException(_("Error in configuration file: site.pyconf\n  %(error)s") % \
+                {'error': str(e) })
+        except IOError as error:
+            e = str(error)
+            if "site.pyconf" in e :
+                e += "\nYou can copy data" + cfg.VARS.sep + "site.template.pyconf to data" + cfg.VARS.sep + "site.pyconf and edit the file"
+            raise src.SatException( e );
+        
+        # add user local path for configPath
+        site_cfg.SITE.config.configPath.append(os.path.join(cfg.VARS.personalDir, 'Applications'), "User applications path")
+        
+        merger.merge(cfg, site_cfg)
+
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["SITE"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+  
+        
+        # =======================================================================================
+        # Load APPLICATION config file
+        if application is not None:
+            # search APPLICATION file in all directories in configPath
+            cp = cfg.SITE.config.configPath
+            src.pyconf.streamOpener = ConfigOpener(cp)
+            try:
+                application_cfg = src.pyconf.Config(application + '.pyconf')
+            except IOError as e:
+                raise src.SatException(_("%s, use 'config --list' to get the list of available applications.") %e)
+            except src.pyconf.ConfigError as e:
+                raise src.SatException(_("Error in configuration file: %(application)s.pyconf\n  %(error)s") % \
+                    { 'application': application, 'error': str(e) } )
+
+            merger.merge(cfg, application_cfg)
+
+            # apply overwrite from command line if needed
+            for rule in self.get_command_line_overrides(options, ["APPLICATION"]):
+                exec('cfg.' + rule) # this cannot be factorized because of the exec
+        
+        # =======================================================================================
+        # Load softwares config files in SOFTWARE section
+       
+        # The directory containing the softwares definition
+        softsDir = os.path.join(cfg.VARS.dataDir, 'softwares')
+        
+        # Loop on all files that are in softsDir directory and read their config
+        for fName in os.listdir(softsDir):
+            if fName.endswith(".pyconf"):
+                src.pyconf.streamOpener = ConfigOpener([softsDir])
+                try:
+                    soft_cfg = src.pyconf.Config(open(os.path.join(softsDir, fName)))
+                except src.pyconf.ConfigError as e:
+                    raise src.SatException(_("Error in configuration file: %(soft)s\n  %(error)s") % \
+                        {'soft' :  fName, 'error': str(e) })
+                except IOError as error:
+                    e = str(error)
+                    raise src.SatException( e );
+                
+                merger.merge(cfg, soft_cfg)
+
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["SOFTWARE"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+
+        
+        # =======================================================================================
+        # load USER config
+        self.setUserConfigFile(cfg)
+        user_cfg_file = self.getUserConfigFile()
+        user_cfg = src.pyconf.Config(open(user_cfg_file))
+        merger.merge(cfg, user_cfg)
+
+        # apply overwrite from command line if needed
+        for rule in self.get_command_line_overrides(options, ["USER"]):
+            exec('cfg.' + rule) # this cannot be factorize because of the exec
+
+        return cfg
+
+    def setUserConfigFile(self, config):
+        '''Set the user config file name and path.
+        If necessary, build it from another one or create it from scratch.
+        
+        :param config class 'src.pyconf.Config': The global config (containing all pyconf).
+        '''
+        # get the expected name and path of the file
+        self.config_file_name = 'salomeTools.pyconf'
+        self.user_config_file_path = os.path.join(config.VARS.personalDir, self.config_file_name)
+        
+        # if pyconf does not exist, create it from scratch
+        if not os.path.isfile(self.user_config_file_path): 
+            self.createConfigFile(config)
+    
+    def createConfigFile(self, config):
+        '''This method is called when there are no user config file. It build it from scratch.
+        
+        :param config class 'src.pyconf.Config': The global config.
+        :return: the config corresponding to the file created.
+        :rtype: config class 'src.pyconf.Config'
+        '''
+        
+        cfg_name = self.getUserConfigFile()
+
+        user_cfg = src.pyconf.Config()
+        #
+        user_cfg.addMapping('USER', src.pyconf.Mapping(user_cfg), "")
+
+        #
+        user_cfg.USER.addMapping('workDir', os.path.expanduser('~'),
+            "This is where salomeTools will work. You may (and probably do) change it.\n")
+        user_cfg.USER.addMapping('cvs_user', config.VARS.user,
+            "This is the user name used to access salome cvs base.\n")
+        user_cfg.USER.addMapping('svn_user', config.VARS.user,
+            "This is the user name used to access salome svn base.\n")
+        user_cfg.USER.addMapping('output_level', 3,
+            "This is the default output_level you want. 0=>no output, 5=>debug.\n")
+        user_cfg.USER.addMapping('publish_dir', os.path.join(os.path.expanduser('~'), 'websupport', 'satreport'), "")
+        user_cfg.USER.addMapping('editor', 'vi', "This is the editor used to modify configuration files\n")
+        user_cfg.USER.addMapping('browser', 'firefox', "This is the browser used to read html documentation\n")
+        user_cfg.USER.addMapping('pdf_viewer', 'evince', "This is the pdf_viewer used to read pdf documentation\n")
+        # 
+        src.ensure_path_exists(config.VARS.personalDir)
+        src.ensure_path_exists(os.path.join(config.VARS.personalDir, 'Applications'))
+
+        f = open(cfg_name, 'w')
+        user_cfg.__save__(f)
+        f.close()
+        print(_("You can edit it to configure salomeTools (use: sat config --edit).\n"))
+
+        return user_cfg   
+
+    def getUserConfigFile(self):
+        '''Get the user config file
+        :return: path to the user config file.
+        :rtype: str
+        '''
+        if not self.user_config_file_path:
+            raise src.SatException(_("Error in getUserConfigFile: missing user config file path"))
+        return self.user_config_file_path     
+        
+    
+def print_value(config, path, show_label, level=0, show_full_path=False):
+    '''Prints a value from the configuration. Prints recursively the values under the initial path.
+    
+    :param config class 'src.pyconf.Config': The configuration from which the value is displayed.
+    :param path str : the path in the configuration of the value to print.
+    :param show_label boolean: if True, do a basic display. (useful for bash completion)
+    :param level int: The number of spaces to add before display.
+    :param show_full_path :
+    '''            
+    
+    # display all the path or not
+    if show_full_path:
+        vname = path
+    else:
+        vname = path.split('.')[-1]
+
+    # number of spaces before the display
+    tab_level = "  " * level
+    
+    # call to the function that gets the value of the path.
+    try:
+        val = config.getByPath(path)
+    except Exception as e:
+        sys.stdout.write(tab_level)
+        sys.stdout.write("%s: ERROR %s\n" % (src.printcolors.printcLabel(vname), src.printcolors.printcError(str(e))))
+        return
+
+    # in this case, display only the value
+    if show_label:
+        sys.stdout.write(tab_level)
+        sys.stdout.write("%s: " % src.printcolors.printcLabel(vname))
+
+    # The case where the value has under values, do a recursive call to the function
+    if dir(val).__contains__('keys'):
+        if show_label: sys.stdout.write("\n")
+        for v in sorted(val.keys()):
+            print_value(config, path + '.' + v, show_label, level + 1)
+    elif val.__class__ == src.pyconf.Sequence or isinstance(val, list): # in this case, value is a list (or a Sequence)
+        if show_label: sys.stdout.write("\n")
+        index = 0
+        for v in val:
+            print_value(config, path + "[" + str(index) + "]", show_label, level + 1)
+            index = index + 1
+    else: # case where val is just a str
+        sys.stdout.write("%s\n" % val)
+
+def description():
+    '''method that is called when salomeTools is called with --help option.
+    
+    :return: The text to display for the config command description.
+    :rtype: str
+    '''
+    return _("The config command allows manipulation and operation on config files.")
+    
+
+def run(args, runner):
+    '''method that is called when salomeTools is called with config parameter.
+    '''
+    # Parse the options
+    (options, args) = parser.parse_args(args)
+    
+    # case : print a value of the config
+    if options.value:
+        if options.value == ".":
+            # if argument is ".", print all the config
+            for val in sorted(runner.cfg.keys()):
+                print_value(runner.cfg, val, True)
+            return
+        print_value(runner.cfg, options.value, True, level=0, show_full_path=False)
+        return
+    
+    # case : edit user pyconf file or application file
+    elif options.edit:
+        editor = runner.cfg.USER.editor
+        if 'APPLICATION' not in runner.cfg: # edit user pyconf
+            usercfg = os.path.join(runner.cfg.VARS.personalDir, 'salomeTools.pyconf')
+            src.system.show_in_editor(editor, usercfg)
+        else:
+            # search for file <application>.pyconf and open it
+            for path in runner.cfg.SITE.config.configPath:
+                pyconf_path = os.path.join(path, runner.cfg.VARS.application + ".pyconf")
+                if os.path.exists(pyconf_path):
+                    src.system.show_in_editor(editor, pyconf_path)
+                    break
+    
+    # case : copy an existing <application>.pyconf to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
+    elif options.copy:
+        # product is required
+        src.check_config_has_application( runner.cfg )
+
+        # get application file path 
+        source = runner.cfg.VARS.application + '.pyconf'
+        source_full_path = ""
+        for path in runner.cfg.SITE.config.configPath:
+            # ignore personal directory
+            if path == runner.cfg.VARS.personalDir:
+                continue
+            # loop on all directories that can have pyconf applications
+            zz = os.path.join(path, source)
+            if os.path.exists(zz):
+                source_full_path = zz
+                break
+
+        if len(source_full_path) == 0:
+            raise src.SatException(_("Config file for product %s not found\n") % source)
+        else:
+            if len(args) > 0:
+                # a name is given as parameter, use it
+                dest = args[0]
+            elif 'copy_prefix' in runner.cfg.SITE.config:
+                # use prefix
+                dest = runner.cfg.SITE.config.copy_prefix + runner.cfg.VARS.application
+            else:
+                # use same name as source
+                dest = runner.cfg.VARS.application
+                
+            # the full path
+            dest_file = os.path.join(runner.cfg.VARS.personalDir, 'Applications', dest + '.pyconf')
+            if os.path.exists(dest_file):
+                raise src.SatException(_("A personal application '%s' already exists") % dest)
+            
+            # perform the copy
+            shutil.copyfile(source_full_path, dest_file)
+            print(_("%s has been created.") % dest_file)
+    
+    # case : display all the available pyconf applications
+    elif options.list:
+        lproduct = list()
+        # search in all directories that can have pyconf applications
+        for path in runner.cfg.SITE.config.configPath:
+            # print a header
+            sys.stdout.write("------ %s\n" % src.printcolors.printcHeader(path))
+
+            if not os.path.exists(path):
+                sys.stdout.write(src.printcolors.printcError(_("Directory not found")) + "\n")
+            else:
+                for f in sorted(os.listdir(path)):
+                    # ignore file that does not ends with .pyconf
+                    if not f.endswith('.pyconf'):
+                        continue
+
+                    appliname = f[:-len('.pyconf')]
+                    if appliname not in lproduct:
+                        lproduct.append(appliname)
+                        if path.startswith(runner.cfg.VARS.personalDir):
+                            sys.stdout.write("%s*\n" % appliname)
+                        else:
+                            sys.stdout.write("%s\n" % appliname)
+
+            sys.stdout.write("\n")
+
+    
+    
\ No newline at end of file
diff --git a/commands/test_command.py b/commands/test_command.py
new file mode 100644 (file)
index 0000000..560cd45
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+import src
+
+# Define all possible option for config command :  sat config <options>
+parser = src.options.Options()
+parser.add_option('u', 'unique', 'boolean', 'unique', "TEST d'option.")
+parser.add_option('v', 'value', 'string', 'value', "Appelle la commande config avec l'option value.")
+
+def description():
+    return _("Test d'une commande supplémentaire.")
+    
+
+def run(args, runner):
+    (options, args) = parser.parse_args(args)
+    if options.unique:
+        print('unique')
+    elif options.value:
+        runner.cfg.VARS.user = 'TEST'
+        runner.config('-v ' + options.value)
diff --git a/salomeTools.py b/salomeTools.py
new file mode 100755 (executable)
index 0000000..ac6f4cc
--- /dev/null
@@ -0,0 +1,277 @@
+#!/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
+
+'''This file is the main entry file to salomeTools
+'''
+
+# python imports
+import os
+import sys
+import imp
+import types
+import gettext
+
+# salomeTools imports
+import src.options
+
+# get path to salomeTools sources
+satdir  = os.path.dirname(os.path.realpath(__file__))
+cmdsdir = os.path.join(satdir, 'commands')
+
+# Make the src package accessible from all code
+sys.path.append(satdir)
+sys.path.append(cmdsdir)
+
+import config
+
+# load resources for internationalization
+#es = gettext.translation('salomeTools', os.path.join(satdir, 'src', 'i18n'))
+#es.install()
+gettext.install('salomeTools', os.path.join(satdir, 'src', 'i18n'))
+
+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.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)
+
+# Define all possible option for salomeTools command :  sat <option> <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."))
+
+class Sat(object):
+    '''The main class that stores all the commands of salomeTools
+    '''
+    def __init__(self, opt='', dataDir=None):
+        '''Initialization
+        
+        :param opt str: The sat options 
+        :param: dataDir str : the directory that contain all the external data (like software pyconf and software scripts)
+        '''
+        # Read the salomeTools options (the list of possible options is at the beginning of this file)
+        try:
+            (options, argus) = parser.parse_args(opt.split(' '))
+        except Exception as exc:
+            write_exception(exc)
+            sys.exit(-1)
+
+        # initialization of class attributes       
+        self.__dict__ = dict()
+        self.cfg = None # the config that will be read using pyconf module
+        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)
+        
+        # if the help option has been called, print help and exit
+        if options.help:
+            try:
+                self.print_help(argus)
+                sys.exit(0)
+            except Exception as exc:
+                write_exception(exc)
+                sys.exit(1)
+
+    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(name + _(" is not a valid command"))
+    
+    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:
+            # 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=''):
+                '''The function that will load the configuration (all pyconf)
+                and return the function run of the command corresponding to module
+                
+                :param args str: The directory path containing the commands 
+                '''
+                argv = args.split(" ")
+                
+                # if it is provided by the command line, get the application
+                appliToLoad = None
+                if argv != [''] and argv[0][0] != "-":
+                    appliToLoad = argv[0].rstrip('*')
+                    argv = argv[1:]
+                
+                # Read the config if it is not already done
+                if not self.cfg:
+                    # read the configuration from all the pyconf files    
+                    cfgManager = config.ConfigManager()
+                    self.cfg = cfgManager.getConfig(dataDir=self.dataDir, application=appliToLoad, options=self.options, command=__nameCmd__)
+                
+                return __module__.run(argv, self)
+
+            # 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 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 = config.ConfigManager()
+        self.cfg = cfgManager.getConfig(dataDir=self.dataDir)
+
+        # Check if this command exists
+        if not hasattr(self, command):
+            raise src.SatException(_("Command '%s' does not exist") % command)
+        
+        # Print salomeTools version
+        print_version()
+        
+        # load the module
+        module = self.get_module(command)
+
+        # print the description of the command that is done in the command file
+        if hasattr( module, "description" ) :
+            print(src.printcolors.printcHeader( _("Description:") ))
+            print(module.description() + '\n')
+
+        # print the description of the command options
+        if hasattr( module, "parser" ) :
+            module.parser.print_help()
+
+    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 print_version():
+    '''prints salomeTools version (in src/internal_config/salomeTools.pyconf)
+    '''
+    # read the config 
+    cfgManager = config.ConfigManager()
+    cfg = cfgManager.getConfig()
+    # print the key corresponding to salomeTools version
+    print(src.printcolors.printcHeader( _("Version: ") ) + cfg.INTERNAL.sat_version + '\n')
+
+
+def print_help():
+    '''prints salomeTools general help
+    
+    :param options str: the options
+    '''
+    print_version()
+    
+    print(src.printcolors.printcHeader( _("Usage: ") ) + "sat [sat_options] <command> [product] [command_options]\n")
+
+    parser.print_help()
+
+    # display all the available commands.
+    print(src.printcolors.printcHeader(_("Available commands are:\n")))
+    for command in lCommand:
+        print(" - %s" % (command))
+        
+    # Explain how to get the help for a specific command
+    print(src.printcolors.printcHeader(_("\nGetting the help for a specific command: ")) + "sat --help <command>\n")
+
+def write_exception(exc):
+    '''write 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")
+
+# ###############################
+# MAIN : terminal command usage #
+# ###############################
+if __name__ == "__main__":  
+    # Get the command line using sys.argv
+    cmd_line = " ".join(sys.argv)
+    # Initialize the code that will be returned by the terminal command 
+    code = 0
+    (options, args) = parser.parse_args(sys.argv[1:])
+    
+    # no arguments : print general help
+    if len(args) == 0:
+        print_help()
+        sys.exit(0)
+    
+    # instantiate the salomeTools class with correct options
+    sat = Sat(' '.join(sys.argv[1:]))
+    # the command called
+    command = args[0]
+    # get dynamically the command function to call
+    fun_command = sat.__getattr__(command)
+    # call the command with two cases : mode debug or not
+    if options.debug_mode:
+        # call classically the command and if it fails, show exception and stack (usual python mode)
+        code = fun_command(' '.join(args[1:]))
+    else:
+        # catch exception in order to show less verbose but elegant message
+        try:
+            code = fun_command(' '.join(args[1:]))
+        except Exception as exc:
+            code = 1
+            write_exception(exc)
+    
+    # exit salomeTools with the right code (0 if no errors, else 1)
+    if code is None: code = 0
+    sys.exit(code)
+        
\ No newline at end of file
diff --git a/sat b/sat
index 804194add49f463fbb65bf71df637980790b116f..19cd1ab237a6dee557b56423fd42d2beb877d616 120000 (symlink)
--- a/sat
+++ b/sat
@@ -1 +1 @@
-src/salomeTools.py
\ No newline at end of file
+salomeTools.py
\ No newline at end of file
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..629fb4f40ed1338be3f9c3d7313bb3cdd105a869 100644 (file)
@@ -0,0 +1,49 @@
+#!/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
+
+from . import pyconf
+from . import architecture
+from . import printcolors
+from . import options
+from . import system
+
+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.mkdir(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 )
\ No newline at end of file
diff --git a/src/architecture.py b/src/architecture.py
new file mode 100644 (file)
index 0000000..d10b06c
--- /dev/null
@@ -0,0 +1,133 @@
+#!/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 : all the stuff that can change with the architecture on which SAT is running
+'''
+
+import os, sys, platform, pwd
+
+def is_windows():
+    '''method that checks windows OS
+      
+    :rtype: boolean
+    '''
+    return platform.system() == 'Windows'
+
+def get_user():
+    '''method that gets the username that launched sat  
+    
+    :rtype: str
+    '''
+    # In windows case, the USERNAME environment variable has to be set
+    if is_windows():
+        if not os.environ.has_key('USERNAME'):
+            raise Exception('USERNAME environment variable not set')
+        return os.environ['USERNAME']
+    else: # linux
+        return pwd.getpwuid(os.getuid())[0]
+
+def _lsb_release(args):
+    '''Get system information with lsb_release.
+    
+    :param args str: The arguments to give to lsb_release.
+    :return: The distribution.
+    :rtype: str
+    '''
+    try:
+        path = '/usr/local/bin:/usr/bin:/bin'
+        lsb_path = os.getenv("LSB_PATH")
+        if lsb_path is not None:
+            path = lsb_path + ":" + path
+        
+        from subprocess import Popen, PIPE
+        res = Popen(['lsb_release', args], env={'PATH': path}, stdout=PIPE).communicate()[0][:-1]
+        # in case of python3, convert byte to str
+        if isinstance(res, bytes):
+            res = res.decode()
+        return res
+    except OSError:
+        sys.stderr.write(_(u"lsb_release not installed\n"))
+        sys.stderr.write(_(u"You can define $LSB_PATH to give the path to lsb_release\n"))
+        sys.exit(-1)
+
+def get_distribution(codes):
+    '''Gets the code for the distribution
+    
+    :param codes L{Mapping}: The map containing distribution correlation table.
+    :return: The distribution on which salomeTools is running, regarding the distribution correlation table contained in codes variable.
+    :rtype: str
+    '''
+    if is_windows():
+        return "Win"
+
+    # Call to lsb_release
+    distrib = _lsb_release('-si')
+    if codes is not None and distrib in codes:
+        distrib = codes[distrib]
+    else:
+        sys.stderr.write(_(u"Unknown distribution: '%s'\n") % distrib)
+        sys.stderr.write(_(u"Please add your distribution to data/distrib.pyconf\n"))
+        sys.exit(-1)
+
+    return distrib
+
+
+def get_distrib_version(distrib, codes):
+    '''Gets the version of the distribution
+    
+    :param distrib str: The distribution on which the version will be found.
+    :param codes L{Mapping}: The map containing distribution correlation table.
+    :return: The version of the distribution on which salomeTools is running, regarding the distribution correlation table contained in codes variable.
+    :rtype: str
+    '''
+
+    if is_windows():
+        return platform.release()
+
+    # Call to lsb_release
+    version = _lsb_release('-sr')
+    if distrib in codes:
+        if version in codes[distrib]:
+            version = codes[distrib][version]
+
+    return version
+
+def get_python_version():
+    '''Gets the version of the running python.
+    
+    :return: the version of the running python.
+    :rtype: str
+    '''
+    
+    # The platform python module gives the answer
+    return platform.python_version()
+
+def get_nb_proc():
+    '''Gets the number of processors of the machine on which salomeTools is running.
+    
+    :return: the number of processors.
+    :rtype: str
+    '''
+    
+    try :
+        import multiprocessing
+        nb_proc=multiprocessing.cpu_count()
+    except :
+        nb_proc=int(os.sysconf('SC_NPROCESSORS_ONLN'))
+    return nb_proc
\ No newline at end of file
diff --git a/src/common/__init__.py b/src/common/__init__.py
deleted file mode 100644 (file)
index 629fb4f..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import os
-
-from . import pyconf
-from . import architecture
-from . import printcolors
-from . import options
-from . import system
-
-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.mkdir(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 )
\ No newline at end of file
diff --git a/src/common/architecture.py b/src/common/architecture.py
deleted file mode 100644 (file)
index d10b06c..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-'''
-In this file : all the stuff that can change with the architecture on which SAT is running
-'''
-
-import os, sys, platform, pwd
-
-def is_windows():
-    '''method that checks windows OS
-      
-    :rtype: boolean
-    '''
-    return platform.system() == 'Windows'
-
-def get_user():
-    '''method that gets the username that launched sat  
-    
-    :rtype: str
-    '''
-    # In windows case, the USERNAME environment variable has to be set
-    if is_windows():
-        if not os.environ.has_key('USERNAME'):
-            raise Exception('USERNAME environment variable not set')
-        return os.environ['USERNAME']
-    else: # linux
-        return pwd.getpwuid(os.getuid())[0]
-
-def _lsb_release(args):
-    '''Get system information with lsb_release.
-    
-    :param args str: The arguments to give to lsb_release.
-    :return: The distribution.
-    :rtype: str
-    '''
-    try:
-        path = '/usr/local/bin:/usr/bin:/bin'
-        lsb_path = os.getenv("LSB_PATH")
-        if lsb_path is not None:
-            path = lsb_path + ":" + path
-        
-        from subprocess import Popen, PIPE
-        res = Popen(['lsb_release', args], env={'PATH': path}, stdout=PIPE).communicate()[0][:-1]
-        # in case of python3, convert byte to str
-        if isinstance(res, bytes):
-            res = res.decode()
-        return res
-    except OSError:
-        sys.stderr.write(_(u"lsb_release not installed\n"))
-        sys.stderr.write(_(u"You can define $LSB_PATH to give the path to lsb_release\n"))
-        sys.exit(-1)
-
-def get_distribution(codes):
-    '''Gets the code for the distribution
-    
-    :param codes L{Mapping}: The map containing distribution correlation table.
-    :return: The distribution on which salomeTools is running, regarding the distribution correlation table contained in codes variable.
-    :rtype: str
-    '''
-    if is_windows():
-        return "Win"
-
-    # Call to lsb_release
-    distrib = _lsb_release('-si')
-    if codes is not None and distrib in codes:
-        distrib = codes[distrib]
-    else:
-        sys.stderr.write(_(u"Unknown distribution: '%s'\n") % distrib)
-        sys.stderr.write(_(u"Please add your distribution to data/distrib.pyconf\n"))
-        sys.exit(-1)
-
-    return distrib
-
-
-def get_distrib_version(distrib, codes):
-    '''Gets the version of the distribution
-    
-    :param distrib str: The distribution on which the version will be found.
-    :param codes L{Mapping}: The map containing distribution correlation table.
-    :return: The version of the distribution on which salomeTools is running, regarding the distribution correlation table contained in codes variable.
-    :rtype: str
-    '''
-
-    if is_windows():
-        return platform.release()
-
-    # Call to lsb_release
-    version = _lsb_release('-sr')
-    if distrib in codes:
-        if version in codes[distrib]:
-            version = codes[distrib][version]
-
-    return version
-
-def get_python_version():
-    '''Gets the version of the running python.
-    
-    :return: the version of the running python.
-    :rtype: str
-    '''
-    
-    # The platform python module gives the answer
-    return platform.python_version()
-
-def get_nb_proc():
-    '''Gets the number of processors of the machine on which salomeTools is running.
-    
-    :return: the number of processors.
-    :rtype: str
-    '''
-    
-    try :
-        import multiprocessing
-        nb_proc=multiprocessing.cpu_count()
-    except :
-        nb_proc=int(os.sysconf('SC_NPROCESSORS_ONLN'))
-    return nb_proc
\ No newline at end of file
diff --git a/src/common/i18n/build_strings.sh b/src/common/i18n/build_strings.sh
deleted file mode 100755 (executable)
index 9e2a95f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-# This script gets the strings to internationalise from the source code
-
-I18HOME=`dirname $0`
-SRC_DIR=$I18HOME/../..
-
-# get strings for french translation
-echo Build strings for French
-
-poFile=$I18HOME/fr/LC_MESSAGES/salomeTools.po
-refFile=$I18HOME/fr/LC_MESSAGES/ref.pot
-
-xgettext $SRC_DIR/*.py $SRC_DIR/common/*.py \
-    --no-wrap --no-location --language=Python --omit-header \
-    --output=$refFile
-msgmerge -q --update $poFile $refFile
-msgattrib --no-obsolete -o $poFile $poFile
-rm $refFile
-
diff --git a/src/common/i18n/fr/LC_MESSAGES/salomeTools.mo b/src/common/i18n/fr/LC_MESSAGES/salomeTools.mo
deleted file mode 100644 (file)
index a63767d..0000000
Binary files a/src/common/i18n/fr/LC_MESSAGES/salomeTools.mo and /dev/null differ
diff --git a/src/common/i18n/fr/LC_MESSAGES/salomeTools.po b/src/common/i18n/fr/LC_MESSAGES/salomeTools.po
deleted file mode 100644 (file)
index 594e6c8..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-msgid "shows global help or help on a specific command."
-msgstr "affiche l'aide gnrale ou pour une commande spcifique."
-
-msgid " is not a valid command"
-msgstr " n'est pas une commande valide"
-
-msgid "Usage: "
-msgstr "Utilisation : "
-
-msgid "Available commands are:\n"
-msgstr "Les commandes disponibles sont:\n"
-
-msgid ""
-"\n"
-"Getting the help for a specific command: "
-msgstr ""
-"\n"
-"Obtenir l'aide d'une commande spcifique : "
-
-msgid "lsb_release not installed\n"
-msgstr "lsb_release n'est pas install\n"
-
-msgid "You can define $LSB_PATH to give the path to lsb_release\n"
-msgstr "Vous pouvez dfinir $LSB_PATH pour donner le chemin vers lsb_release\n"
-
-#, python-format
-msgid "Unknown distribution: '%s'\n"
-msgstr "Distribution inconnue : '%s'\n"
-
-msgid "Please add your distribution to data/distrib.pyconf\n"
-msgstr "SVP ajoutez votre distribution au fichier data/distrib.pyconf\n"
-
-#, python-format
-msgid "Unknown architecture: '%s'\n"
-msgstr "Architecture inconnue: '%s'\n"
-
-msgid " is not a valid option"
-msgstr " n'est pas une option valide"
-
-msgid "Available options are:"
-msgstr "Les options disponibles sont:"
diff --git a/src/common/i18n/translate.py b/src/common/i18n/translate.py
deleted file mode 100644 (file)
index a7a8171..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#/bin/env python
-import polib
-po = polib.pofile('fr/LC_MESSAGES/salomeTools.po', encoding='utf-8')
-po.save_as_mofile('fr/LC_MESSAGES/salomeTools.mo')
diff --git a/src/common/i18n/translate.sh b/src/common/i18n/translate.sh
deleted file mode 100755 (executable)
index 5ad2b2c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-# this script creates the binary resources file from the strings file
-
-I18HOME=`dirname $0`
-
-# french
-echo build French ressources
-msgfmt ${I18HOME}/fr/LC_MESSAGES/salomeTools.po -o ${I18HOME}/fr/LC_MESSAGES/salomeTools.mo
diff --git a/src/common/internal_config/distrib.pyconf b/src/common/internal_config/distrib.pyconf
deleted file mode 100644 (file)
index 18e6edd..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-# distribution codes
-# Code to use to replace the value returned by: lsb_release -si
-# If no code is found an error will be raised.
-DISTRIBUTIONS :
-{
-    "bullxServer": "BS"
-    "CentOS": "CO"
-    "Debian": "DB"
-    "Fedora": "FD"
-    "LinuxMint": "MN"
-    "Mageia": "MG"
-    "MandrivaLinux": "MD"
-    "RedHatEnterpriseServer": "RH"
-    "Ubuntu": "UB"
-    "openSUSE project":"OS"
-}
-
-# versions substitution
-# Code to use for release, to the replace value returned by: lsb_release -sr
-# If no code is found the value is used.
-VERSIONS :
-{
-    "DB":
-    {
-        "unstable": "sid"
-        "5.0": "05"
-        "4.0": "04"
-           "6.0.7" : "06"
-           "6.0.10": "06"
-           "7.1" : "07"
-           "7.5" : "07"
-    }
-    "MD":
-    {
-        "2008.0": "08"
-        "2010.0": "10"
-        "2010.1": "10"
-        "2010.2": "10"
-        "2007.1": "07"
-        "2006.0": "06"
-        "4" : "06"
-    }
-    "MG":
-    {
-        "3": "03"
-        "4": "04"
-        "5": "05"
-        "6": "06"
-    }
-    "CO":
-    {
-        "7.1.1503": "7.1"
-    }
-}
diff --git a/src/common/internal_config/salomeTools.pyconf b/src/common/internal_config/salomeTools.pyconf
deleted file mode 100644 (file)
index 9aa08bf..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-INTERNAL :
-{   
-    sat_version : "5.0.0dev"
-}
-
diff --git a/src/common/options.py b/src/common/options.py
deleted file mode 100644 (file)
index b54b190..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-'''The Options class that manages the access to all options passed as parameters in salomeTools command lines
-'''
-import getopt
-import sys
-from . import printcolors
-
-class OptResult(object):
-    '''An instance of this class will be the object manipulated in code of all salomeTools commands
-    The aim of this class is to have an elegant syntax to manipulate the options. 
-    ex: 
-    print(options.level)
-    5
-    '''
-    def __init__(self):
-        '''Initialization
-        '''
-        self.__dict__ = dict()
-
-    def __getattr__(self, name):
-        '''Overwrite of the __getattr__ function to customize it for option usage
-        
-        :param name str: The attribute to get the value.
-        :return: the value corresponding to the attribute.
-        :rtype: str,int,list,boolean
-        '''
-        if name in self.__dict__:
-            return self.__dict__[name]
-        else:
-            raise AttributeError(name + _(u" is not a valid option"))
-
-    def __setattr__(self, name, value):
-        '''Overwrite of the __setattr__ function to customize it for option usage
-        
-        :param name str: The attribute to set.
-        :param value str: The value  corresponding to the attribute.
-        :return: Nothing.
-        :rtype: N\A
-        '''
-        object.__setattr__(self,name,value)
-
-class Options:
-    '''Class to manage all salomeTools options
-    '''
-    def __init__(self):
-        '''Initialization
-        '''
-        # The options field stocks all options of a command in a list that contains dicts
-        self.options = []
-        # The list of available option type
-        self.availableOptions = ["boolean", "string", "int", "float", "long", "list", "list2"]
-
-    def add_option(self, shortName, longName, optionType, destName, helpString=""):
-        '''Method to add an option to a command. It gets all attributes of an option and append it in the options field
-        
-        :param shortName str: The short name of the option (ex "l" for level option).
-        :param longName str: The long name of the option (ex "level" for level option).
-        :param optionType str: The type of the option (ex "int").
-        :param destName str: The name that will be used in the code.
-        :param helpString str: The text to display when user ask for help on a command.     
-        :return: Nothing.
-        :rtype: N\A
-        '''
-        option = dict()
-        option['shortName'] = shortName
-        option['longName'] = longName
-
-        if optionType not in self.availableOptions:
-            print("error optionType", optionType, "not available.")
-            sys.exit(-1)
-
-        option['optionType'] = optionType
-        option['destName'] = destName
-        option['helpString'] = helpString
-        option['result'] = None
-        self.options.append(option)
-
-    def print_help(self):
-        '''Method that display all options stored in self.options and there help
-        
-        :return: Nothing.
-        :rtype: N\A
-        '''
-        # Do nothing if there are no options
-        if len(self.options) == 0:
-            return
-
-        # for all options, print its values. "shortname" is an optional field of the options 
-        print(printcolors.printcHeader(_("Available options are:")))
-        for option in self.options:
-            if 'shortName' in option and len(option['shortName']) > 0:
-                print(" -%(shortName)1s, --%(longName)s (%(optionType)s)\n\t%(helpString)s\n" % option)
-            else:
-                print(" --%(longName)s (%(optionType)s)\n\t%(helpString)s\n" % option)
-
-    def parse_args(self, argList=None):
-        '''Method that instantiates the class OptResult that gives access to all options in the code
-        
-        :param argList list: the raw list of arguments that were passed
-        :return: optResult, args : optResult is the option instance to manipulate in the code. args is the full raw list of passed options 
-        :rtype: (class 'common.options.OptResult',list)
-        '''
-        if argList is None:
-            argList = sys.argv[1:]
-        
-        # format shortNameOption and longNameOption to make right arguments to getopt.getopt function
-        shortNameOption = ""
-        longNameOption = []
-        for option in self.options:
-            shortNameOption = shortNameOption + option['shortName']
-            if option['shortName'] != "" and option['optionType'] != "boolean":
-                shortNameOption = shortNameOption + ":"
-
-            if option['longName'] != "":
-                if option['optionType'] != "boolean":
-                    longNameOption.append(option['longName'] + "=")
-                else:
-                    longNameOption.append(option['longName'])
-
-        # call to getopt.getopt function to get the option passed in the command regarding the available options
-        optlist, args = getopt.getopt(argList, shortNameOption, longNameOption)
-        
-        # instantiate and completing the optResult that will be returned
-        optResult = OptResult()
-        for option in self.options:
-            shortOption = "-" + option['shortName']
-            longOption = "--" + option['longName']
-            optionType = option['optionType']
-            for opt in optlist:
-                if opt[0] in [shortOption, longOption]:
-                    if optionType == "string":
-                        option['result'] = opt[1]
-                    elif optionType == "boolean":
-                        option['result'] = True
-                    elif optionType == "int":
-                        option['result'] = int(opt[1])
-                    elif optionType == "float":
-                        option['result'] = float(opt[1])
-                    elif optionType == "long":
-                        option['result'] = long(opt[1])
-                    elif optionType == "list":
-                        if option['result'] is None:
-                            option['result'] = list()
-                        option['result'].append(opt[1])
-                    elif optionType == "list2":
-                        if option['result'] is None:
-                            option['result'] = list()
-                        if opt[1].find(",") == -1:
-                            option['result'].append(opt[1])
-                        else:
-                            elts = filter(lambda l: len(l) > 0, opt[1].split(","))
-                            option['result'].extend(elts)
-
-            optResult.__setattr__(option['destName'], option['result'])
-            # free the option in order to be able to make a new free call of options (API case)
-            option['result'] = None
-        return optResult, args
-
diff --git a/src/common/printcolors.py b/src/common/printcolors.py
deleted file mode 100755 (executable)
index da76a47..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-'''In this file is stored the mechanism that manage color prints in the terminal
-'''
-
-# define constant to use in scripts
-COLOR_ERROR = 'ERROR'
-COLOR_WARNING = 'WARNING'
-COLOR_SUCCESS = 'SUCCESS'
-COLOR_LABEL = 'LABEL'
-COLOR_HEADER = 'HEADER'
-COLOR_INFO = 'INFO'
-COLOR_HIGLIGHT = 'HIGHLIGHT'
-
-# the color map to use to print the colors
-__colormap__ = {
-    COLOR_ERROR: '\033[1m\033[31m',
-    COLOR_SUCCESS: '\033[1m\033[32m',
-    COLOR_WARNING: '\033[33m',
-    COLOR_HEADER: '\033[34m',
-    COLOR_INFO: '\033[35m',
-    COLOR_LABEL: '\033[36m',
-    COLOR_HIGLIGHT: '\033[97m\033[43m'
-}
-
-# list of available codes
-__code_range__ = [1, 4] + list(range(30, 38)) + list(range(40, 48)) + list(range(90, 98)) + list(range(100, 108))
-
-def printc(txt, code=''):
-    '''print a text with colors
-    
-    :param txt str: The text to be printed.
-    :param code str: The color to use.
-    :return: The colored text.
-    :rtype: str
-    '''
-    # no code means 'auto mode' (works only for OK, KO, NO and ERR*)
-    if code == '':
-        striptxt = txt.strip().upper()
-        if striptxt == "OK":
-            code = COLOR_SUCCESS
-        elif striptxt in ["KO", "NO"] or striptxt.startswith("ERR"):
-            code = COLOR_ERROR
-        else:
-            return txt
-
-    # no code => output the originial text
-    if code not in __colormap__.keys() or __colormap__[code] == '':
-        return txt
-
-    return __colormap__[code] + txt + '\033[0m'
-
-def printcInfo(txt):
-    '''print a text info color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_INFO)
-
-def printcError(txt):
-    '''print a text error color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_ERROR)
-
-def printcWarning(txt):
-    '''print a text warning color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_WARNING)
-
-def printcHeader(txt):
-    '''print a text header color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_HEADER)
-
-def printcLabel(txt):
-    '''print a text label color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_LABEL)
-
-def printcSuccess(txt):
-    '''print a text success color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_SUCCESS)
-
-def printcHighlight(txt):
-    '''print a text highlight color
-    
-    :param txt str: The text to be printed.
-    :return: The colored text.
-    :rtype: str
-    '''
-    return printc(txt, COLOR_HIGLIGHT)
-
-def cleancolor(message):
-    '''remove color from a colored text.
-    
-    :param message str: The text to be cleaned.
-    :return: The cleaned text.
-    :rtype: str
-    '''
-    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("}")
-
-
diff --git a/src/common/pyconf.py b/src/common/pyconf.py
deleted file mode 100644 (file)
index 821f3d6..0000000
+++ /dev/null
@@ -1,1723 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation, and that the name of Vinay Sajip
-# not be used in advertising or publicity pertaining to distribution
-# of the software without specific, written prior permission.
-# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
-# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-#  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
-
-# CEA adds : 
-# Possibility to overwrites value in a pyconf file
-# Python 3 porting
-
-
-"""
-This is a configuration module for Python.
-
-This module should work under Python versions >= 2.2, and cannot be used with
-earlier versions since it uses new-style classes.
-
-Development and testing has only been carried out (so far) on Python 2.3.4 and
-Python 2.4.2. See the test module (test_config.py) included in the
-U{distribution<http://www.red-dove.com/python_config.html|_blank>} (follow the
-download link).
-
-A simple example - with the example configuration file::
-
-    messages:
-    [
-      {
-        stream : `sys.stderr`
-        message: 'Welcome'
-        name: 'Harry'
-      }
-      {
-        stream : `sys.stdout`
-        message: 'Welkom'
-        name: 'Ruud'
-      }
-      {
-        stream : $messages[0].stream
-        message: 'Bienvenue'
-        name: Yves
-      }
-    ]
-
-a program to read the configuration would be::
-
-    from config import Config
-
-    f = file('simple.cfg')
-    cfg = Config(f)
-    for m in cfg.messages:
-        s = '%s, %s' % (m.message, m.name)
-        try:
-            print >> m.stream, s
-        except IOError, e:
-            print e
-
-which, when run, would yield the console output::
-
-    Welcome, Harry
-    Welkom, Ruud
-    Bienvenue, Yves
-
-See U{this tutorial<http://www.red-dove.com/python_config.html|_blank>} for more
-information.
-
-#modified for salomeTools
-@version: 0.3.7.1
-
-@author: Vinay Sajip
-
-@copyright: Copyright (C) 2004-2007 Vinay Sajip. All Rights Reserved.
-
-
-@var streamOpener: The default stream opener. This is a factory function which
-takes a string (e.g. filename) and returns a stream suitable for reading. If
-unable to open the stream, an IOError exception should be thrown.
-
-The default value of this variable is L{defaultStreamOpener}. For an example
-of how it's used, see test_config.py (search for streamOpener).
-"""
-
-__author__  = "Vinay Sajip <vinay_sajip@red-dove.com>"
-__status__  = "alpha"
-__version__ = "0.3.7.1" #modified for salomeTools
-__date__    = "05 October 2007"
-
-import codecs
-import os
-import sys
-
-WORD = 'a'
-NUMBER = '9'
-STRING = '"'
-EOF = ''
-LCURLY = '{'
-RCURLY = '}'
-LBRACK = '['
-LBRACK2 = 'a['
-RBRACK = ']'
-LPAREN = '('
-LPAREN2 = '(('
-RPAREN = ')'
-DOT = '.'
-COMMA = ','
-COLON = ':'
-AT = '@'
-PLUS = '+'
-MINUS = '-'
-STAR = '*'
-SLASH = '/'
-MOD = '%'
-BACKTICK = '`'
-DOLLAR = '$'
-TRUE = 'True'
-FALSE = 'False'
-NONE = 'None'
-
-WORDCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
-
-if sys.platform == 'win32':
-    NEWLINE = '\r\n'
-elif os.name == 'mac':
-    NEWLINE = '\r'
-else:
-    NEWLINE = '\n'
-
-try:
-    has_utf32 = True
-except:
-    has_utf32 = False
-
-class ConfigInputStream(object):
-    """
-    An input stream which can read either ANSI files with default encoding
-    or Unicode files with BOMs.
-
-    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
-    built-in support.
-    """
-    def __init__(self, stream):
-        """
-        Initialize an instance.
-
-        @param stream: The underlying stream to be read. Should be seekable.
-        @type stream: A stream (file-like object).
-        """
-        encoding = None
-        signature = stream.read(4)
-        used = -1
-        if has_utf32:
-            if signature == codecs.BOM_UTF32_LE:
-                encoding = 'utf-32le'
-            elif signature == codecs.BOM_UTF32_BE:
-                encoding = 'utf-32be'
-        if encoding is None:
-            if signature[:3] == codecs.BOM_UTF8:
-                used = 3
-                encoding = 'utf-8'
-            elif signature[:2] == codecs.BOM_UTF16_LE:
-                used = 2
-                encoding = 'utf-16le'
-            elif signature[:2] == codecs.BOM_UTF16_BE:
-                used = 2
-                encoding = 'utf-16be'
-            else:
-                used = 0
-        if used >= 0:
-            stream.seek(used)
-        if encoding:
-            reader = codecs.getreader(encoding)
-            stream = reader(stream)
-        self.stream = stream
-        self.encoding = encoding
-
-    def read(self, size):
-        if (size == 0) or (self.encoding is None):
-            rv = self.stream.read(size)
-        else:
-            rv = u''
-            while size > 0:
-                rv += self.stream.read(1)
-                size -= 1
-        return rv
-
-    def close(self):
-        self.stream.close()
-
-    def readline(self):
-        if self.encoding is None:
-            line = ''
-        else:
-            line = u''
-        while True:
-            c = self.stream.read(1)
-            if isinstance(c, bytes):
-                c = c.decode()
-            if c:
-                line += c
-            if c == '\n':
-                break
-        return line
-
-class ConfigOutputStream(object):
-    """
-    An output stream which can write either ANSI files with default encoding
-    or Unicode files with BOMs.
-
-    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
-    built-in support.
-    """
-
-    def __init__(self, stream, encoding=None):
-        """
-        Initialize an instance.
-
-        @param stream: The underlying stream to be written.
-        @type stream: A stream (file-like object).
-        @param encoding: The desired encoding.
-        @type encoding: str
-        """
-        if encoding is not None:
-            encoding = str(encoding).lower()
-        self.encoding = encoding
-        if encoding == "utf-8":
-            stream.write(codecs.BOM_UTF8)
-        elif encoding == "utf-16be":
-            stream.write(codecs.BOM_UTF16_BE)
-        elif encoding == "utf-16le":
-            stream.write(codecs.BOM_UTF16_LE)
-        elif encoding == "utf-32be":
-            stream.write(codecs.BOM_UTF32_BE)
-        elif encoding == "utf-32le":
-            stream.write(codecs.BOM_UTF32_LE)
-
-        if encoding is not None:
-            writer = codecs.getwriter(encoding)
-            stream = writer(stream)
-        self.stream = stream
-
-    def write(self, data):
-        self.stream.write(data)
-
-    def flush(self):
-        self.stream.flush()
-
-    def close(self):
-        self.stream.close()
-
-def defaultStreamOpener(name):
-    """
-    This function returns a read-only stream, given its name. The name passed
-    in should correspond to an existing stream, otherwise an exception will be
-    raised.
-
-    This is the default value of L{streamOpener}; assign your own callable to
-    streamOpener to return streams based on names. For example, you could use
-    urllib2.urlopen().
-
-    @param name: The name of a stream, most commonly a file name.
-    @type name: str
-    @return: A stream with the specified name.
-    @rtype: A read-only stream (file-like object)
-    """
-    return ConfigInputStream(open(name, 'rb'))
-
-streamOpener = None
-
-__resolveOverwrite__ = True
-
-class ConfigError(Exception):
-    """
-    This is the base class of exceptions raised by this module.
-    """
-    pass
-
-class ConfigFormatError(ConfigError):
-    """
-    This is the base class of exceptions raised due to syntax errors in
-    configurations.
-    """
-    pass
-
-class ConfigResolutionError(ConfigError):
-    """
-    This is the base class of exceptions raised due to semantic errors in
-    configurations.
-    """
-    pass
-
-def isWord(s):
-    """
-    See if a passed-in value is an identifier. If the value passed in is not a
-    string, False is returned. An identifier consists of alphanumerics or
-    underscore characters.
-
-    Examples::
-
-        isWord('a word') ->False
-        isWord('award') -> True
-        isWord(9) -> False
-        isWord('a_b_c_') ->True
-
-    @note: isWord('9abc') will return True - not exactly correct, but adequate
-    for the way it's used here.
-
-    @param s: The name to be tested
-    @type s: any
-    @return: True if a word, else False
-    @rtype: bool
-    """
-    if type(s) != type(''):
-        return False
-    s = s.replace('_', '')
-    return s.isalnum()
-
-def makePath(prefix, suffix):
-    """
-    Make a path from a prefix and suffix.
-
-    Examples::
-
-        makePath('', 'suffix') -> 'suffix'
-        makePath('prefix', 'suffix') -> 'prefix.suffix'
-        makePath('prefix', '[1]') -> 'prefix[1]'
-
-    @param prefix:  The prefix to use. If it evaluates as false, the suffix
-                    is returned.
-    @type prefix:   str
-    @param suffix:  The suffix to use. It is either an identifier or an
-                    index in brackets.
-    @type suffix:   str
-    @return:        The path concatenation of prefix and suffix, with a
-                    dot if the suffix is not a bracketed index.
-    @rtype:         str
-
-    """
-    if not prefix:
-        rv = suffix
-    elif suffix[0] == '[':
-        rv = prefix + suffix
-    else:
-        rv = prefix + '.' + suffix
-    return rv
-
-
-class Container(object):
-    """
-    This internal class is the base class for mappings and sequences.
-
-    @ivar path: A string which describes how to get
-    to this instance from the root of the hierarchy.
-
-    Example::
-
-        a.list.of[1].or['more'].elements
-    """
-    def __init__(self, parent):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        object.__setattr__(self, 'parent', parent)
-
-    def setPath(self, path):
-        """
-        Set the path for this instance.
-        @param path: The path - a string which describes how to get
-        to this instance from the root of the hierarchy.
-        @type path: str
-        """
-        object.__setattr__(self, 'path', path)
-
-    def evaluate(self, item):
-        """
-        Evaluate items which are instances of L{Reference} or L{Expression}.
-
-        L{Reference} instances are evaluated using L{Reference.resolve},
-        and L{Expression} instances are evaluated using
-        L{Expression.evaluate}.
-
-        @param item: The item to be evaluated.
-        @type item: any
-        @return: If the item is an instance of L{Reference} or L{Expression},
-        the evaluated value is returned, otherwise the item is returned
-        unchanged.
-        """
-        if isinstance(item, Reference):
-            item = item.resolve(self)
-        elif isinstance(item, Expression):
-            item = item.evaluate(self)
-        return item
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        @raise NotImplementedError: If a subclass does not override this
-        """
-        raise NotImplementedError
-
-    def writeValue(self, value, stream, indent):
-        if isinstance(self, Mapping):
-            indstr = ' '
-        else:
-            indstr = indent * '  '
-        if isinstance(value, Reference) or isinstance(value, Expression):
-            stream.write('%s%r%s' % (indstr, value, NEWLINE))
-        else:
-            if isinstance(value, str): # and not isWord(value):
-                value = repr(value)
-            stream.write('%s%s%s' % (indstr, value, NEWLINE))
-
-class Mapping(Container):
-    """
-    This internal class implements key-value mappings in configurations.
-    """
-
-    def __init__(self, parent=None):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        Container.__init__(self, parent)
-        object.__setattr__(self, 'path', '')
-        object.__setattr__(self, 'data', {})
-        object.__setattr__(self, 'order', [])   # to preserve ordering
-        object.__setattr__(self, 'comments', {})
-
-    def __delitem__(self, key):
-        """
-        Remove an item
-        """
-        data = object.__getattribute__(self, 'data')
-        if key not in data:
-            raise AttributeError(key)
-        order = object.__getattribute__(self, 'order')
-        comments = object.__getattribute__(self, 'comments')
-        del data[key]
-        order.remove(key)
-        del comments[key]
-
-    def __getitem__(self, key):
-        data = object.__getattribute__(self, 'data')
-        if key not in data:
-            raise AttributeError(key)
-        rv = data[key]
-        return self.evaluate(rv)
-
-    __getattr__ = __getitem__
-    
-    '''
-    def __getattribute__(self, name):
-        if name == "__dict__":
-            return {}
-        if name in ["__methods__", "__members__"]:
-            return []
-        #if name == "__class__":
-        #    return ''
-        data = object.__getattribute__(self, "data")
-        useData = data.has_key(name)
-        if useData:
-            rv = getattr(data, name)
-        else:
-            rv = object.__getattribute__(self, name)
-            if rv is None:
-                raise AttributeError(name)
-        return rv
-    '''
-
-    def iteritems(self):
-        for key in self.keys():
-            yield(key, self[key])
-        raise StopIteration
-
-    def __contains__(self, item):
-        order = object.__getattribute__(self, 'order')
-        return item in order
-
-    def addMapping(self, key, value, comment, setting=False):
-        """
-        Add a key-value mapping with a comment.
-
-        @param key: The key for the mapping.
-        @type key: str
-        @param value: The value for the mapping.
-        @type value: any
-        @param comment: The comment for the key (can be None).
-        @type comment: str
-        @param setting: If True, ignore clashes. This is set
-        to true when called from L{__setattr__}.
-        @raise ConfigFormatError: If an existing key is seen
-        again and setting is False.
-        """
-        data = object.__getattribute__(self, 'data')
-        order = object.__getattribute__(self, 'order')
-        comments = object.__getattribute__(self, 'comments')
-
-        data[key] = value
-        if key not in order:
-            order.append(key)
-        elif not setting:
-            raise ConfigFormatError("repeated key: %s" % key)
-        comments[key] = comment
-
-    def __setattr__(self, name, value):
-        self.addMapping(name, value, None, True)
-
-    __setitem__ = __setattr__
-
-    def keys(self):
-        """
-        Return the keys in a similar way to a dictionary.
-        """
-        return object.__getattribute__(self, 'order')
-
-    def get(self, key, default=None):
-        """
-        Allows a dictionary-style get operation.
-        """
-        if key in self:
-            return self[key]
-        return default
-
-    def __str__(self):
-        return str(object.__getattribute__(self, 'data'))
-
-    def __repr__(self):
-        return repr(object.__getattribute__(self, 'data'))
-
-    def __len__(self):
-        return len(object.__getattribute__(self, 'order'))
-
-    def __iter__(self):
-        return self.iterkeys()
-
-    def iterkeys(self):
-        order = object.__getattribute__(self, 'order')
-        return order.__iter__()
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        """
-        indstr = indent * '  '
-        if len(self) == 0:
-            stream.write(' { }%s' % NEWLINE)
-        else:
-            if isinstance(container, Mapping):
-                stream.write(NEWLINE)
-            stream.write('%s{%s' % (indstr, NEWLINE))
-            self.__save__(stream, indent + 1)
-            stream.write('%s}%s' % (indstr, NEWLINE))
-
-    def __save__(self, stream, indent=0):
-        """
-        Save this configuration to the specified stream.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output.
-        @type indent: int
-        """
-        indstr = indent * '  '
-        order = object.__getattribute__(self, 'order')
-        data = object.__getattribute__(self, 'data')
-        maxlen = 0 # max(map(lambda x: len(x), order))
-        for key in order:
-            comment = self.comments[key]
-            if isWord(key):
-                skey = key
-            else:
-                skey = repr(key)
-            if comment:
-                stream.write('%s#%s' % (indstr, comment))
-            stream.write('%s%-*s :' % (indstr, maxlen, skey))
-            value = data[key]
-            if isinstance(value, Container):
-                value.writeToStream(stream, indent, self)
-            else:
-                self.writeValue(value, stream, indent)
-
-class Config(Mapping):
-    """
-    This class represents a configuration, and is the only one which clients
-    need to interface to, under normal circumstances.
-    """
-
-    class Namespace(object):
-        """
-        This internal class is used for implementing default namespaces.
-
-        An instance acts as a namespace.
-        """
-        def __init__(self):
-            self.sys = sys
-            self.os = os
-
-    def __init__(self, streamOrFile=None, parent=None):
-        """
-        Initializes an instance.
-
-        @param streamOrFile: If specified, causes this instance to be loaded
-        from the stream (by calling L{load}). If a string is provided, it is
-        passed to L{streamOpener} to open a stream. Otherwise, the passed
-        value is assumed to be a stream and used as is.
-        @type streamOrFile: A readable stream (file-like object) or a name.
-        @param parent: If specified, this becomes the parent of this instance
-        in the configuration hierarchy.
-        @type parent: a L{Container} instance.
-        """
-        Mapping.__init__(self, parent)
-        object.__setattr__(self, 'reader', ConfigReader(self))
-        object.__setattr__(self, 'namespaces', [Config.Namespace()])
-        if streamOrFile is not None:
-            if isinstance(streamOrFile, str) or isinstance(streamOrFile, bytes):
-                global streamOpener
-                if streamOpener is None:
-                    streamOpener = defaultStreamOpener
-                streamOrFile = streamOpener(streamOrFile)
-            load = object.__getattribute__(self, "load")
-            load(streamOrFile)
-
-    def load(self, stream):
-        """
-        Load the configuration from the specified stream. Multiple streams can
-        be used to populate the same instance, as long as there are no
-        clashing keys. The stream is closed.
-        @param stream: A stream from which the configuration is read.
-        @type stream: A read-only stream (file-like object).
-        @raise ConfigError: if keys in the loaded configuration clash with
-        existing keys.
-        @raise ConfigFormatError: if there is a syntax error in the stream.
-        """
-        reader = object.__getattribute__(self, 'reader')
-        reader.load(stream)
-        stream.close()
-
-    def addNamespace(self, ns, name=None):
-        """
-        Add a namespace to this configuration which can be used to evaluate
-        (resolve) dotted-identifier expressions.
-        @param ns: The namespace to be added.
-        @type ns: A module or other namespace suitable for passing as an
-        argument to vars().
-        @param name: A name for the namespace, which, if specified, provides
-        an additional level of indirection.
-        @type name: str
-        """
-        namespaces = object.__getattribute__(self, 'namespaces')
-        if name is None:
-            namespaces.append(ns)
-        else:
-            setattr(namespaces[0], name, ns)
-
-    def removeNamespace(self, ns, name=None):
-        """
-        Remove a namespace added with L{addNamespace}.
-        @param ns: The namespace to be removed.
-        @param name: The name which was specified when L{addNamespace} was
-        called.
-        @type name: str
-        """
-        namespaces = object.__getattribute__(self, 'namespaces')
-        if name is None:
-            namespaces.remove(ns)
-        else:
-            delattr(namespaces[0], name)
-
-    def __save__(self, stream, indent=0, no_close=False):
-        """
-        Save this configuration to the specified stream. The stream is
-        closed if this is the top-level configuration in the hierarchy.
-        L{Mapping.__save__} is called to do all the work.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output.
-        @type indent: int
-        """
-        Mapping.__save__(self, stream, indent)
-        if indent == 0 and not no_close:
-            stream.close()
-
-    def getByPath(self, path):
-        """
-        Obtain a value in the configuration via its path.
-        @param path: The path of the required value
-        @type path: str
-        @return the value at the specified path.
-        @rtype: any
-        @raise ConfigError: If the path is invalid
-        """
-        s = 'self.' + path
-        try:
-            return eval(s)
-        except Exception as e:
-            raise ConfigError(str(e))
-
-class Sequence(Container):
-    """
-    This internal class implements a value which is a sequence of other values.
-    """
-    class SeqIter(object):
-        """
-        This internal class implements an iterator for a L{Sequence} instance.
-        """
-        def __init__(self, seq):
-            self.seq = seq
-            self.limit = len(object.__getattribute__(seq, 'data'))
-            self.index = 0
-
-        def __iter__(self):
-            return self
-
-        def next(self):
-            if self.index >= self.limit:
-                raise StopIteration
-            rv = self.seq[self.index]
-            self.index += 1
-            return rv
-        
-        # This method is for python3 compatibility
-        def __next__(self): 
-            if self.index >= self.limit:
-                raise StopIteration
-            rv = self.seq[self.index]
-            self.index += 1
-            return rv
-
-    def __init__(self, parent=None):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        Container.__init__(self, parent)
-        object.__setattr__(self, 'data', [])
-        object.__setattr__(self, 'comments', [])
-
-    def append(self, item, comment):
-        """
-        Add an item to the sequence.
-
-        @param item: The item to add.
-        @type item: any
-        @param comment: A comment for the item.
-        @type comment: str
-        """
-        data = object.__getattribute__(self, 'data')
-        comments = object.__getattribute__(self, 'comments')
-        data.append(item)
-        comments.append(comment)
-
-    def __getitem__(self, index):
-        data = object.__getattribute__(self, 'data')
-        try:
-            rv = data[index]
-        except (IndexError, KeyError, TypeError):
-            raise ConfigResolutionError('%r is not a valid index for %r' % (index, object.__getattribute__(self, 'path')))
-        if not isinstance(rv, list):
-            rv = self.evaluate(rv)
-        else:
-            # deal with a slice
-            result = []
-            for a in rv:
-                result.append(self.evaluate(a))
-            rv = result
-        return rv
-
-    def __iter__(self):
-        return Sequence.SeqIter(self)
-
-    def __repr__(self):
-        return repr(object.__getattribute__(self, 'data'))
-
-    def __str__(self):
-        return str(self[:]) # using the slice evaluates the contents
-
-    def __len__(self):
-        return len(object.__getattribute__(self, 'data'))
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        """
-        indstr = indent * '  '
-        if len(self) == 0:
-            stream.write(' [ ]%s' % NEWLINE)
-        else:
-            if isinstance(container, Mapping):
-                stream.write(NEWLINE)
-            stream.write('%s[%s' % (indstr, NEWLINE))
-            self.__save__(stream, indent + 1)
-            stream.write('%s]%s' % (indstr, NEWLINE))
-
-    def __save__(self, stream, indent):
-        """
-        Save this instance to the specified stream.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output, > 0
-        @type indent: int
-        """
-        if indent == 0:
-            raise ConfigError("sequence cannot be saved as a top-level item")
-        data = object.__getattribute__(self, 'data')
-        comments = object.__getattribute__(self, 'comments')
-        indstr = indent * '  '
-        for i in xrange(0, len(data)):
-            value = data[i]
-            comment = comments[i]
-            if comment:
-                stream.write('%s#%s' % (indstr, comment))
-            if isinstance(value, Container):
-                value.writeToStream(stream, indent, self)
-            else:
-                self.writeValue(value, stream, indent)
-
-class Reference(object):
-    """
-    This internal class implements a value which is a reference to another value.
-    """
-    def __init__(self, config, type, ident):
-        """
-        Initialize an instance.
-
-        @param config: The configuration which contains this reference.
-        @type config: A L{Config} instance.
-        @param type: The type of reference.
-        @type type: BACKTICK or DOLLAR
-        @param ident: The identifier which starts the reference.
-        @type ident: str
-        """
-        self.config = config
-        self.type = type
-        self.elements = [ident]
-
-    def addElement(self, type, ident):
-        """
-        Add an element to the reference.
-
-        @param type: The type of reference.
-        @type type: BACKTICK or DOLLAR
-        @param ident: The identifier which continues the reference.
-        @type ident: str
-        """
-        self.elements.append((type, ident))
-
-    def findConfig(self, container):
-        """
-        Find the closest enclosing configuration to the specified container.
-
-        @param container: The container to start from.
-        @type container: L{Container}
-        @return: The closest enclosing configuration, or None.
-        @rtype: L{Config}
-        """
-        while (container is not None) and not isinstance(container, Config):
-            container = object.__getattribute__(container, 'parent')
-        return container
-
-    def resolve(self, container):
-        """
-        Resolve this instance in the context of a container.
-
-        @param container: The container to resolve from.
-        @type container: L{Container}
-        @return: The resolved value.
-        @rtype: any
-        @raise ConfigResolutionError: If resolution fails.
-        """
-        rv = None
-        path = object.__getattribute__(container, 'path')
-        current = container
-        while current is not None:
-            if self.type == BACKTICK:
-                namespaces = object.__getattribute__(current, 'namespaces')
-                found = False
-                for ns in namespaces:
-                    try:
-                        rv = eval(str(self)[1:-1], vars(ns))
-                        found = True
-                        break
-                    except:
-                        pass
-                if found:
-                    break
-            else:
-                key = self.elements[0]
-                try:
-                    rv = current[key]
-                    for item in self.elements[1:]:
-                        key = item[1]
-                        rv = rv[key]
-                    break
-                except:
-                    rv = None
-                    pass
-            current = object.__getattribute__(current, 'parent')
-        if current is None:
-            raise ConfigResolutionError("unable to evaluate %r in the configuration %s" % (self, path))
-        return rv
-
-    def __str__(self):
-        s = self.elements[0]
-        for tt, tv in self.elements[1:]:
-            if tt == DOT:
-                s += '.%s' % tv
-            else:
-                s += '[%r]' % tv
-        if self.type == BACKTICK:
-            return BACKTICK + s + BACKTICK
-        else:
-            return DOLLAR + s
-
-    def __repr__(self):
-        return self.__str__()
-
-class Expression(object):
-    """
-    This internal class implements a value which is obtained by evaluating an expression.
-    """
-    def __init__(self, op, lhs, rhs):
-        """
-        Initialize an instance.
-
-        @param op: the operation expressed in the expression.
-        @type op: PLUS, MINUS, STAR, SLASH, MOD
-        @param lhs: the left-hand-side operand of the expression.
-        @type lhs: any Expression or primary value.
-        @param rhs: the right-hand-side operand of the expression.
-        @type rhs: any Expression or primary value.
-        """
-        self.op = op
-        self.lhs = lhs
-        self.rhs = rhs
-
-    def __str__(self):
-        return '%r %s %r' % (self.lhs, self.op, self.rhs)
-
-    def __repr__(self):
-        return self.__str__()
-
-    def evaluate(self, container):
-        """
-        Evaluate this instance in the context of a container.
-
-        @param container: The container to evaluate in from.
-        @type container: L{Container}
-        @return: The evaluated value.
-        @rtype: any
-        @raise ConfigResolutionError: If evaluation fails.
-        @raise ZeroDivideError: If division by zero occurs.
-        @raise TypeError: If the operation is invalid, e.g.
-        subtracting one string from another.
-        """
-        lhs = self.lhs
-        if isinstance(lhs, Reference):
-            lhs = lhs.resolve(container)
-        elif isinstance(lhs, Expression):
-            lhs = lhs.evaluate(container)
-        rhs = self.rhs
-        if isinstance(rhs, Reference):
-            rhs = rhs.resolve(container)
-        elif isinstance(rhs, Expression):
-            rhs = rhs.evaluate(container)
-        op = self.op
-        if op == PLUS:
-            rv = lhs + rhs
-        elif op == MINUS:
-            rv = lhs - rhs
-        elif op == STAR:
-            rv = lhs * rhs
-        elif op == SLASH:
-            rv = lhs / rhs
-        else:
-            rv = lhs % rhs
-        return rv
-
-class ConfigReader(object):
-    """
-    This internal class implements a parser for configurations.
-    """
-
-    def __init__(self, config):
-        self.filename = None
-        self.config = config
-        self.lineno = 0
-        self.colno = 0
-        self.lastc = None
-        self.last_token = None
-        self.commentchars = '#'
-        self.whitespace = ' \t\r\n'
-        self.quotes = '\'"'
-        self.punct = ':-+*/%,.{}[]()@`$'
-        self.digits = '0123456789'
-        self.wordchars = '%s' % WORDCHARS # make a copy
-        self.identchars = self.wordchars + self.digits
-        self.pbchars = []
-        self.pbtokens = []
-        self.comment = None
-
-    def location(self):
-        """
-        Return the current location (filename, line, column) in the stream
-        as a string.
-
-        Used when printing error messages,
-
-        @return: A string representing a location in the stream being read.
-        @rtype: str
-        """
-        return "%s(%d,%d)" % (self.filename, self.lineno, self.colno)
-
-    def getChar(self):
-        """
-        Get the next char from the stream. Update line and column numbers
-        appropriately.
-
-        @return: The next character from the stream.
-        @rtype: str
-        """
-        if self.pbchars:
-            c = self.pbchars.pop()
-            if isinstance(c,bytes):
-                c = c.decode()
-        else:
-            c = self.stream.read(1)
-            if isinstance(c,bytes):
-                try:
-                    c = c.decode()
-                except:
-                    import pdb;pdb.set_trace()
-            self.colno += 1
-            if c == '\n':
-                self.lineno += 1
-                self.colno = 1
-        return c
-
-    def __repr__(self):
-        return "<ConfigReader at 0x%08x>" % id(self)
-
-    __str__ = __repr__
-
-    def getToken(self):
-        """
-        Get a token from the stream. String values are returned in a form
-        where you need to eval() the returned value to get the actual
-        string. The return value is (token_type, token_value).
-
-        Multiline string tokenizing is thanks to David Janes (BlogMatrix)
-
-        @return: The next token.
-        @rtype: A token tuple.
-        """
-        if self.pbtokens:
-            return self.pbtokens.pop()
-        stream = self.stream
-        self.comment = None
-        token = ''
-        tt = EOF
-        while True:
-            c = self.getChar()
-            if not c:
-                break
-            elif c == '#':
-                if self.comment :
-                    self.comment += '#' + stream.readline()
-                else :
-                    self.comment = stream.readline()
-                self.lineno += 1
-                continue
-            if c in self.quotes:
-                token = c
-                quote = c
-                tt = STRING
-                escaped = False
-                multiline = False
-                c1 = self.getChar()
-                if c1 == quote:
-                    c2 = self.getChar()
-                    if c2 == quote:
-                        multiline = True
-                        token += quote
-                        token += quote
-                    else:
-                        self.pbchars.append(c2)
-                        self.pbchars.append(c1)
-                else:
-                    self.pbchars.append(c1)
-                while True:
-                    c = self.getChar()
-                    if not c:
-                        break
-                    token += c
-                    if (c == quote) and not escaped:
-                        if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
-                            break
-                    if c == '\\':
-                        escaped = not escaped
-                    else:
-                        escaped = False
-                if not c:
-                    raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
-                break
-            if c in self.whitespace:
-                self.lastc = c
-                continue
-            elif c in self.punct:
-                token = c
-                tt = c
-                if (self.lastc == ']') or (self.lastc in self.identchars):
-                    if c == '[':
-                        tt = LBRACK2
-                    elif c == '(':
-                        tt = LPAREN2
-                break
-            elif c in self.digits:
-                token = c
-                tt = NUMBER
-                while True:
-                    c = self.getChar()
-                    if not c:
-                        break
-                    if c in self.digits:
-                        token += c
-                    elif (c == '.') and token.find('.') < 0:
-                        token += c
-                    else:
-                        if c and (c not in self.whitespace):
-                            self.pbchars.append(c)
-                        break
-                break
-            elif c in self.wordchars:
-                token = c
-                tt = WORD
-                c = self.getChar()
-                while c and (c in self.identchars):
-                    token += c
-                    c = self.getChar()
-                if c: # and c not in self.whitespace:
-                    self.pbchars.append(c)
-                if token == "True":
-                    tt = TRUE
-                elif token == "False":
-                    tt = FALSE
-                elif token == "None":
-                    tt = NONE
-                break
-            else:
-                raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
-        if token:
-            self.lastc = token[-1]
-        else:
-            self.lastc = None
-        self.last_token = tt
-        return (tt, token)
-
-    def load(self, stream, parent=None, suffix=None):
-        """
-        Load the configuration from the specified stream.
-
-        @param stream: A stream from which to load the configuration.
-        @type stream: A stream (file-like object).
-        @param parent: The parent of the configuration (to which this reader
-        belongs) in the hierarchy. Specified when the configuration is
-        included in another one.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix of this configuration in the parent
-        configuration. Should be specified whenever the parent is not None.
-        @raise ConfigError: If parent is specified but suffix is not.
-        @raise ConfigFormatError: If there are syntax errors in the stream.
-        """
-        if parent is not None:
-            if suffix is None:
-                raise ConfigError("internal error: load called with parent but no suffix")
-            self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
-        self.setStream(stream)
-        self.token = self.getToken()
-        self.parseMappingBody(self.config)
-        if self.token[0] != EOF:
-            raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
-
-    def setStream(self, stream):
-        """
-        Set the stream to the specified value, and prepare to read from it.
-
-        @param stream: A stream from which to load the configuration.
-        @type stream: A stream (file-like object).
-        """
-        self.stream = stream
-        if hasattr(stream, 'name'):
-            filename = stream.name
-        else:
-            filename = '?'
-        self.filename = filename
-        self.lineno = 1
-        self.colno = 1
-
-    def match(self, t):
-        """
-        Ensure that the current token type matches the specified value, and
-        advance to the next token.
-
-        @param t: The token type to match.
-        @type t: A valid token type.
-        @return: The token which was last read from the stream before this
-        function is called.
-        @rtype: a token tuple - see L{getToken}.
-        @raise ConfigFormatError: If the token does not match what's expected.
-        """
-        if self.token[0] != t:
-            raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
-        rv = self.token
-        self.token = self.getToken()
-        return rv
-
-    def parseMappingBody(self, parent):
-        """
-        Parse the internals of a mapping, and add entries to the provided
-        L{Mapping}.
-
-        @param parent: The mapping to add entries to.
-        @type parent: A L{Mapping} instance.
-        """
-        while self.token[0] in [WORD, STRING]:
-            self.parseKeyValuePair(parent)
-
-    def parseKeyValuePair(self, parent):
-        """
-        Parse a key-value pair, and add it to the provided L{Mapping}.
-
-        @param parent: The mapping to add entries to.
-        @type parent: A L{Mapping} instance.
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        comment = self.comment
-        tt, tv = self.token
-        if tt == WORD:
-            key = tv
-            suffix = tv
-        elif tt == STRING:
-            key = eval(tv)
-            suffix = '[%s]' % tv
-        else:
-            msg = "%s: expecting word or string, found %r"
-            raise ConfigFormatError(msg % (self.location(), tv))
-        self.token = self.getToken()
-        # for now, we allow key on its own as a short form of key : True
-        if self.token[0] == COLON:
-            self.token = self.getToken()
-            value = self.parseValue(parent, suffix)
-        else:
-            value = True
-        try:
-            parent.addMapping(key, value, comment)
-        except Exception as e:
-            raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
-                                    self.token[1]))
-        tt = self.token[0]
-        if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
-            msg = "%s: expecting one of EOF, WORD, STRING, \
-RCURLY, COMMA, found %r"
-            raise ConfigFormatError(msg  % (self.location(), self.token[1]))
-        if tt == COMMA:
-            self.token = self.getToken()
-
-    def parseValue(self, parent, suffix):
-        """
-        Parse a value.
-
-        @param parent: The container to which the value will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: The value
-        @rtype: any
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
-                  TRUE, FALSE, NONE, BACKTICK, MINUS]:
-            rv = self.parseScalar()
-        elif tt == LBRACK:
-            rv = self.parseSequence(parent, suffix)
-        elif tt in [LCURLY, AT]:
-            rv = self.parseMapping(parent, suffix)
-        else:
-            raise ConfigFormatError("%s: unexpected input: %r" %
-               (self.location(), self.token[1]))
-        return rv
-
-    def parseSequence(self, parent, suffix):
-        """
-        Parse a sequence.
-
-        @param parent: The container to which the sequence will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: a L{Sequence} instance representing the sequence.
-        @rtype: L{Sequence}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        rv = Sequence(parent)
-        rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
-        self.match(LBRACK)
-        comment = self.comment
-        tt = self.token[0]
-        while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
-                     TRUE, FALSE, NONE, BACKTICK]:
-            suffix = '[%d]' % len(rv)
-            value = self.parseValue(parent, suffix)
-            rv.append(value, comment)
-            tt = self.token[0]
-            comment = self.comment
-            if tt == COMMA:
-                self.match(COMMA)
-                tt = self.token[0]
-                comment = self.comment
-                continue
-        self.match(RBRACK)
-        return rv
-
-    def parseMapping(self, parent, suffix):
-        """
-        Parse a mapping.
-
-        @param parent: The container to which the mapping will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: a L{Mapping} instance representing the mapping.
-        @rtype: L{Mapping}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        if self.token[0] == LCURLY:
-            self.match(LCURLY)
-            rv = Mapping(parent)
-            rv.setPath(
-               makePath(object.__getattribute__(parent, 'path'), suffix))
-            self.parseMappingBody(rv)
-            self.match(RCURLY)
-        else:
-            self.match(AT)
-            _, fn = self.match(STRING)
-            rv = Config(eval(fn), parent)
-        return rv
-
-    def parseScalar(self):
-        """
-        Parse a scalar - a terminal value such as a string or number, or
-        an L{Expression} or L{Reference}.
-
-        @return: the parsed scalar
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        lhs = self.parseTerm()
-        tt = self.token[0]
-        while tt in [PLUS, MINUS]:
-            self.match(tt)
-            rhs = self.parseTerm()
-            lhs = Expression(tt, lhs, rhs)
-            tt = self.token[0]
-        return lhs
-
-    def parseTerm(self):
-        """
-        Parse a term in an additive expression (a + b, a - b)
-
-        @return: the parsed term
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        lhs = self.parseFactor()
-        tt = self.token[0]
-        while tt in [STAR, SLASH, MOD]:
-            self.match(tt)
-            rhs = self.parseFactor()
-            lhs = Expression(tt, lhs, rhs)
-            tt = self.token[0]
-        return lhs
-
-    def parseFactor(self):
-        """
-        Parse a factor in an multiplicative expression (a * b, a / b, a % b)
-
-        @return: the parsed factor
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
-            rv = self.token[1]
-            if tt != WORD:
-                rv = eval(rv)
-            self.match(tt)
-        elif tt == LPAREN:
-            self.match(LPAREN)
-            rv = self.parseScalar()
-            self.match(RPAREN)
-        elif tt == DOLLAR:
-            self.match(DOLLAR)
-            rv = self.parseReference(DOLLAR)
-        elif tt == BACKTICK:
-            self.match(BACKTICK)
-            rv = self.parseReference(BACKTICK)
-            self.match(BACKTICK)
-        elif tt == MINUS:
-            self.match(MINUS)
-            rv = -self.parseScalar()
-        else:
-            raise ConfigFormatError("%s: unexpected input: %r" %
-               (self.location(), self.token[1]))
-        return rv
-
-    def parseReference(self, type):
-        """
-        Parse a reference.
-
-        @return: the parsed reference
-        @rtype: L{Reference}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        word = self.match(WORD)
-        rv = Reference(self.config, type, word[1])
-        while self.token[0] in [DOT, LBRACK2]:
-            self.parseSuffix(rv)
-        return rv
-
-    def parseSuffix(self, ref):
-        """
-        Parse a reference suffix.
-
-        @param ref: The reference of which this suffix is a part.
-        @type ref: L{Reference}.
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt == DOT:
-            self.match(DOT)
-            word = self.match(WORD)
-            ref.addElement(DOT, word[1])
-        else:
-            self.match(LBRACK2)
-            tt, tv = self.token
-            if tt not in [NUMBER, STRING]:
-                raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
-            self.token = self.getToken()
-            tv = eval(tv)
-            self.match(RBRACK)
-            ref.addElement(LBRACK, tv)
-
-def defaultMergeResolve(map1, map2, key):
-    """
-    A default resolver for merge conflicts. Returns a string
-    indicating what action to take to resolve the conflict.
-
-    @param map1: The map being merged into.
-    @type map1: L{Mapping}.
-    @param map2: The map being used as the merge operand.
-    @type map2: L{Mapping}.
-    @param key: The key in map2 (which also exists in map1).
-    @type key: str
-    @return: One of "merge", "append", "mismatch" or "overwrite"
-             indicating what action should be taken. This should
-             be appropriate to the objects being merged - e.g.
-             there is no point returning "merge" if the two objects
-             are instances of L{Sequence}.
-    @rtype: str
-    """
-    obj1 = map1[key]
-    obj2 = map2[key]
-    if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
-        rv = "merge"
-    elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
-        rv = "append"
-    else:
-        rv = "mismatch"
-    return rv
-
-def overwriteMergeResolve(map1, map2, key):
-    """
-    An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
-    but where a "mismatch" is detected, returns "overwrite" instead.
-
-    @param map1: The map being merged into.
-    @type map1: L{Mapping}.
-    @param map2: The map being used as the merge operand.
-    @type map2: L{Mapping}.
-    @param key: The key in map2 (which also exists in map1).
-    @type key: str
-    """
-    rv = defaultMergeResolve(map1, map2, key)
-    if rv == "mismatch":
-        rv = "overwrite"
-    return rv
-
-class ConfigMerger(object):
-    """
-    This class is used for merging two configurations. If a key exists in the
-    merge operand but not the merge target, then the entry is copied from the
-    merge operand to the merge target. If a key exists in both configurations,
-    then a resolver (a callable) is called to decide how to handle the
-    conflict.
-    """
-
-    def __init__(self, resolver=defaultMergeResolve):
-        """
-        Initialise an instance.
-
-        @param resolver:
-        @type resolver: A callable which takes the argument list
-        (map1, map2, key) where map1 is the mapping being merged into,
-        map2 is the merge operand and key is the clashing key. The callable
-        should return a string indicating how the conflict should be resolved.
-        For possible return values, see L{defaultMergeResolve}. The default
-        value preserves the old behaviour
-        """
-        self.resolver = resolver
-
-    def merge(self, merged, mergee):
-        """
-        Merge two configurations. The second configuration is unchanged,
-        and the first is changed to reflect the results of the merge.
-
-        @param merged: The configuration to merge into.
-        @type merged: L{Config}.
-        @param mergee: The configuration to merge.
-        @type mergee: L{Config}.
-        """
-        self.mergeMapping(merged, mergee)
-
-    def overwriteKeys(self, map1, seq2):
-        """
-        Renint variables. The second mapping is unchanged,
-        and the first is changed depending the keys of the second mapping.
-        @param map1: The mapping to reinit keys into.
-        @type map1: L{Mapping}.
-        @param map2: The mapping container reinit information.
-        @type map2: L{Mapping}.
-        """
-
-        overwrite_list = object.__getattribute__(seq2, 'data')
-        for overwrite_instruction in overwrite_list:
-            object.__setattr__(overwrite_instruction, 'parent', map1)
-            if "__condition__" in overwrite_instruction.keys():
-                overwrite_condition = overwrite_instruction["__condition__"]
-                if eval(overwrite_condition, globals(), map1):
-                    for key in overwrite_instruction.keys():
-                        if key == "__condition__":
-                            continue
-                        try:
-                            exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
-                        except:
-                            exec('map1.' + key + " = " + str(overwrite_instruction[key]))
-            else:
-                for key in overwrite_instruction.keys():
-                    try:
-                        exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
-                    except:
-                        exec('map1.' + key + " = " + str(overwrite_instruction[key]))
-
-    def mergeMapping(self, map1, map2):
-        """
-        Merge two mappings recursively. The second mapping is unchanged,
-        and the first is changed to reflect the results of the merge.
-
-        @param map1: The mapping to merge into.
-        @type map1: L{Mapping}.
-        @param map2: The mapping to merge.
-        @type map2: L{Mapping}.
-        """
-        keys = map1.keys()
-        global __resolveOverwrite__
-        for key in map2.keys():
-            if __resolveOverwrite__ and key == "__overwrite__":
-                self.overwriteKeys(map1,map2[key])
-
-            elif key not in keys:
-                map1[key] = map2[key]
-                if isinstance(map1[key], Container) :
-                    object.__setattr__(map1[key], 'parent', map1)
-            else:
-                obj1 = map1[key]
-                obj2 = map2[key]
-                decision = self.resolver(map1, map2, key)
-                if decision == "merge":
-                    self.mergeMapping(obj1, obj2)
-                elif decision == "append":
-                    self.mergeSequence(obj1, obj2)
-                elif decision == "overwrite":
-                    map1[key] = obj2
-                    if isinstance(map1[key], Container):
-                        object.__setattr__(map1[key], 'parent', map1)
-                elif decision == "mismatch":
-                    self.handleMismatch(obj1, obj2)
-                else:
-                    msg = "unable to merge: don't know how to implement %r"
-                    raise ValueError(msg % decision)
-
-    def mergeSequence(self, seq1, seq2):
-        """
-        Merge two sequences. The second sequence is unchanged,
-        and the first is changed to have the elements of the second
-        appended to it.
-
-        @param seq1: The sequence to merge into.
-        @type seq1: L{Sequence}.
-        @param seq2: The sequence to merge.
-        @type seq2: L{Sequence}.
-        """
-        data1 = object.__getattribute__(seq1, 'data')
-        data2 = object.__getattribute__(seq2, 'data')
-        for obj in data2:
-            data1.append(obj)
-        comment1 = object.__getattribute__(seq1, 'comments')
-        comment2 = object.__getattribute__(seq2, 'comments')
-        for obj in comment2:
-            comment1.append(obj)
-
-    def handleMismatch(self, obj1, obj2):
-        """
-        Handle a mismatch between two objects.
-
-        @param obj1: The object to merge into.
-        @type obj1: any
-        @param obj2: The object to merge.
-        @type obj2: any
-        """
-        raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
-
-class ConfigList(list):
-    """
-    This class implements an ordered list of configurations and allows you
-    to try getting the configuration from each entry in turn, returning
-    the first successfully obtained value.
-    """
-
-    def getByPath(self, path):
-        """
-        Obtain a value from the first configuration in the list which defines
-        it.
-
-        @param path: The path of the value to retrieve.
-        @type path: str
-        @return: The value from the earliest configuration in the list which
-        defines it.
-        @rtype: any
-        @raise ConfigError: If no configuration in the list has an entry with
-        the specified path.
-        """
-        found = False
-        rv = None
-        for entry in self:
-            try:
-                rv = entry.getByPath(path)
-                found = True
-                break
-            except ConfigError:
-                pass
-        if not found:
-            raise ConfigError("unable to resolve %r" % path)
-        return rv
diff --git a/src/common/system.py b/src/common/system.py
deleted file mode 100644 (file)
index cc8a39f..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-'''
-In this file : all functions that do a system call, like open a browser or an editor, or call a git command
-'''
-
-import sys
-import subprocess
-
-
-def show_in_editor(editor, filePath):
-    '''open filePath using editor.
-    
-    :param editor str: The editor to use.
-    :param filePath str: The path to the file to open.
-    '''
-    # default editor is vi
-    if editor is None or len(editor) == 0:
-        editor = 'vi'
-    
-    if '%s' not in editor:
-        editor += ' %s'
-
-    try:
-        # launch cmd using subprocess.Popen
-        cmd = editor % filePath
-        p = subprocess.Popen(cmd, shell=True)
-        p.communicate()
-    except:
-        sys.stderr.write("Unable to edit file %s\n" % filePath)
-    
\ No newline at end of file
diff --git a/src/config.py b/src/config.py
deleted file mode 100644 (file)
index 4884b3b..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import os
-import sys
-import platform
-import datetime
-import shutil
-import gettext
-
-import common
-
-# internationalization
-srcdir = os.path.dirname(os.path.realpath(__file__))
-gettext.install('salomeTools', os.path.join(srcdir, 'common', 'i18n'))
-
-# Define all possible option for config command :  sat config <options>
-parser = common.options.Options()
-parser.add_option('v', 'value', 'string', 'value', _("print the value of CONFIG_VARIABLE."))
-parser.add_option('e', 'edit', 'boolean', 'edit', _("edit the product configuration file."))
-parser.add_option('l', 'list', 'boolean', 'list',_("list all available applications."))
-parser.add_option('c', 'copy', 'boolean', 'copy',
-    _("""copy a config file to the personnal config files directory.
-\tWARNING the included files are not copied.
-\tIf a name is given the new config file takes the given name."""))
-
-class ConfigOpener:
-    ''' Class that helps to find an application pyconf in all the possible directories (pathList)
-    '''
-    def __init__(self, pathList):
-        '''Initialization
-        
-        :param pathList list: The list of paths where to serach a pyconf.
-        '''
-        self.pathList = pathList
-
-    def __call__(self, name):
-        if os.path.isabs(name):
-            return common.pyconf.ConfigInputStream(open(name, 'rb'))
-        else:
-            return common.pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') )
-        raise IOError(_("Configuration file '%s' not found") % name)
-
-    def getPath( self, name ):
-        '''The method that returns the entire path of the pyconf searched
-        :param name str: The name of the searched pyconf.
-        '''
-        for path in self.pathList:
-            if os.path.exists(os.path.join(path, name)):
-                return path
-        raise IOError(_("Configuration file '%s' not found") % name)
-
-class ConfigManager:
-    '''Class that manages the read of all the configuration files of salomeTools
-    '''
-    def __init__(self, dataDir=None):
-        pass
-
-    def _create_vars(self, application=None, command=None, dataDir=None):
-        '''Create a dictionary that stores all information about machine, user, date, repositories, etc...
-        
-        :param application str: The application for which salomeTools is called.
-        :param command str: The command that is called.
-        :param dataDir str: The repository that contain external data for salomeTools.
-        :return: The dictionary that stores all information.
-        :rtype: dict
-        '''
-        var = {}      
-        var['user'] = common.architecture.get_user()
-        var['salometoolsway'] = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-        var['srcDir'] = os.path.join(var['salometoolsway'], 'src')
-        var['sep']= os.path.sep
-        
-        # dataDir has a default location
-        var['dataDir'] = os.path.join(var['salometoolsway'], 'data')
-        if dataDir is not None:
-            var['dataDir'] = dataDir
-
-        var['personalDir'] = os.path.join(os.path.expanduser('~'), '.salomeTools')
-
-        # read linux distributions dictionary
-        distrib_cfg = common.pyconf.Config(os.path.join(var['srcDir'], 'common', 'internal_config', 'distrib.pyconf'))
-        
-        # set platform parameters
-        dist_name = common.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS)
-        dist_version = common.architecture.get_distrib_version(dist_name, codes=distrib_cfg.VERSIONS)
-        dist = dist_name + dist_version
-        
-        var['dist_name'] = dist_name
-        var['dist_version'] = dist_version
-        var['dist'] = dist
-        var['python'] = common.architecture.get_python_version()
-
-        var['nb_proc'] = common.architecture.get_nb_proc()
-        node_name = platform.node()
-        var['node'] = node_name
-        var['hostname'] = node_name
-
-        # set date parameters
-        dt = datetime.datetime.now()
-        var['date'] = dt.strftime('%Y%m%d')
-        var['datehour'] = dt.strftime('%Y%m%d_%H%M%S')
-        var['hour'] = dt.strftime('%H%M%S')
-
-        var['command'] = str(command)
-        var['application'] = str(application)
-
-        # Root dir for temporary files 
-        var['tmp_root'] = os.sep + 'tmp' + os.sep + var['user']
-        # particular win case 
-        if common.architecture.is_windows() : 
-            var['tmp_root'] =  os.path.expanduser('~') + os.sep + 'tmp'
-        
-        return var
-
-    def get_command_line_overrides(self, options, sections):
-        '''get all the overwrites that are in the command line
-        
-        :param options: the options from salomeTools class initialization (like -l5 or --overwrite)
-        :param sections str: The config section to overwrite.
-        :return: The list of all the overwrites to apply.
-        :rtype: list
-        '''
-        # when there are no options or not the overwrite option, return an empty list
-        if options is None or options.overwrite is None:
-            return []
-        
-        over = []
-        for section in sections:
-            # only overwrite the sections that correspond to the option 
-            over.extend(filter(lambda l: l.startswith(section + "."), options.overwrite))
-        return over
-
-    def getConfig(self, application=None, options=None, command=None, dataDir=None):
-        '''get the config from all the configuration files.
-        
-        :param application str: The application for which salomeTools is called.
-        :param options calss Options: The general salomeToosl options (--overwrite or -l5, for example)
-        :param command str: The command that is called.
-        :param dataDir str: The repository that contain external data for salomeTools.
-        :return: The final config.
-        :rtype: class 'common.pyconf.Config'
-        '''        
-        
-        # create a ConfigMerger to handle merge
-        merger = common.pyconf.ConfigMerger()#MergeHandler())
-        
-        # create the configuration instance
-        cfg = common.pyconf.Config()
-        
-        # =======================================================================================
-        # create VARS section
-        var = self._create_vars(application=application, command=command, dataDir=dataDir)
-        # add VARS to config
-        cfg.VARS = common.pyconf.Mapping(cfg)
-        for variable in var:
-            cfg.VARS[variable] = var[variable]
-        
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["VARS"]):
-            exec('cfg.' + rule) # this cannot be factorized because of the exec
-        
-        # =======================================================================================
-        # Load INTERNAL config
-        # read src/common/internal_config/salomeTools.pyconf
-        common.pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'common', 'internal_config')])
-        try:
-            internal_cfg = common.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'common', 'internal_config', 'salomeTools.pyconf')))
-        except common.pyconf.ConfigError as e:
-            raise common.SatException(_("Error in configuration file: salomeTools.pyconf\n  %(error)s") % \
-                {'error': str(e) })
-        
-        merger.merge(cfg, internal_cfg)
-
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["INTERNAL"]):
-            exec('cfg.' + rule) # this cannot be factorized because of the exec        
-        
-        # =======================================================================================
-        # Load SITE config file
-        # search only in the data directory
-        common.pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir])
-        try:
-            site_cfg = common.pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf')))
-        except common.pyconf.ConfigError as e:
-            raise common.SatException(_("Error in configuration file: site.pyconf\n  %(error)s") % \
-                {'error': str(e) })
-        except IOError as error:
-            e = str(error)
-            if "site.pyconf" in e :
-                e += "\nYou can copy data" + cfg.VARS.sep + "site.template.pyconf to data" + cfg.VARS.sep + "site.pyconf and edit the file"
-            raise common.SatException( e );
-        
-        # add user local path for configPath
-        site_cfg.SITE.config.configPath.append(os.path.join(cfg.VARS.personalDir, 'Applications'), "User applications path")
-        
-        merger.merge(cfg, site_cfg)
-
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["SITE"]):
-            exec('cfg.' + rule) # this cannot be factorized because of the exec
-  
-        
-        # =======================================================================================
-        # Load APPLICATION config file
-        if application is not None:
-            # search APPLICATION file in all directories in configPath
-            cp = cfg.SITE.config.configPath
-            common.pyconf.streamOpener = ConfigOpener(cp)
-            try:
-                application_cfg = common.pyconf.Config(application + '.pyconf')
-            except IOError as e:
-                raise common.SatException(_("%s, use 'config --list' to get the list of available applications.") %e)
-            except common.pyconf.ConfigError as e:
-                raise common.SatException(_("Error in configuration file: %(application)s.pyconf\n  %(error)s") % \
-                    { 'application': application, 'error': str(e) } )
-
-            merger.merge(cfg, application_cfg)
-
-            # apply overwrite from command line if needed
-            for rule in self.get_command_line_overrides(options, ["APPLICATION"]):
-                exec('cfg.' + rule) # this cannot be factorized because of the exec
-        
-        # =======================================================================================
-        # Load softwares config files in SOFTWARE section
-       
-        # The directory containing the softwares definition
-        softsDir = os.path.join(cfg.VARS.dataDir, 'softwares')
-        
-        # Loop on all files that are in softsDir directory and read their config
-        for fName in os.listdir(softsDir):
-            if fName.endswith(".pyconf"):
-                common.pyconf.streamOpener = ConfigOpener([softsDir])
-                try:
-                    soft_cfg = common.pyconf.Config(open(os.path.join(softsDir, fName)))
-                except common.pyconf.ConfigError as e:
-                    raise common.SatException(_("Error in configuration file: %(soft)s\n  %(error)s") % \
-                        {'soft' :  fName, 'error': str(e) })
-                except IOError as error:
-                    e = str(error)
-                    raise common.SatException( e );
-                
-                merger.merge(cfg, soft_cfg)
-
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["SOFTWARE"]):
-            exec('cfg.' + rule) # this cannot be factorized because of the exec
-
-        
-        # =======================================================================================
-        # load USER config
-        self.setUserConfigFile(cfg)
-        user_cfg_file = self.getUserConfigFile()
-        user_cfg = common.pyconf.Config(open(user_cfg_file))
-        merger.merge(cfg, user_cfg)
-
-        # apply overwrite from command line if needed
-        for rule in self.get_command_line_overrides(options, ["USER"]):
-            exec('cfg.' + rule) # this cannot be factorize because of the exec
-
-        return cfg
-
-    def setUserConfigFile(self, config):
-        '''Set the user config file name and path.
-        If necessary, build it from another one or create it from scratch.
-        
-        :param config class 'common.pyconf.Config': The global config (containing all pyconf).
-        '''
-        # get the expected name and path of the file
-        self.config_file_name = 'salomeTools.pyconf'
-        self.user_config_file_path = os.path.join(config.VARS.personalDir, self.config_file_name)
-        
-        # if pyconf does not exist, create it from scratch
-        if not os.path.isfile(self.user_config_file_path): 
-            self.createConfigFile(config)
-    
-    def createConfigFile(self, config):
-        '''This method is called when there are no user config file. It build it from scratch.
-        
-        :param config class 'common.pyconf.Config': The global config.
-        :return: the config corresponding to the file created.
-        :rtype: config class 'common.pyconf.Config'
-        '''
-        
-        cfg_name = self.getUserConfigFile()
-
-        user_cfg = common.pyconf.Config()
-        #
-        user_cfg.addMapping('USER', common.pyconf.Mapping(user_cfg), "")
-
-        #
-        user_cfg.USER.addMapping('workDir', os.path.expanduser('~'),
-            "This is where salomeTools will work. You may (and probably do) change it.\n")
-        user_cfg.USER.addMapping('cvs_user', config.VARS.user,
-            "This is the user name used to access salome cvs base.\n")
-        user_cfg.USER.addMapping('svn_user', config.VARS.user,
-            "This is the user name used to access salome svn base.\n")
-        user_cfg.USER.addMapping('output_level', 3,
-            "This is the default output_level you want. 0=>no output, 5=>debug.\n")
-        user_cfg.USER.addMapping('publish_dir', os.path.join(os.path.expanduser('~'), 'websupport', 'satreport'), "")
-        user_cfg.USER.addMapping('editor', 'vi', "This is the editor used to modify configuration files\n")
-        user_cfg.USER.addMapping('browser', 'firefox', "This is the browser used to read html documentation\n")
-        user_cfg.USER.addMapping('pdf_viewer', 'evince', "This is the pdf_viewer used to read pdf documentation\n")
-        # 
-        common.ensure_path_exists(config.VARS.personalDir)
-        common.ensure_path_exists(os.path.join(config.VARS.personalDir, 'Applications'))
-
-        f = open(cfg_name, 'w')
-        user_cfg.__save__(f)
-        f.close()
-        print(_("You can edit it to configure salomeTools (use: sat config --edit).\n"))
-
-        return user_cfg   
-
-    def getUserConfigFile(self):
-        '''Get the user config file
-        :return: path to the user config file.
-        :rtype: str
-        '''
-        if not self.user_config_file_path:
-            raise common.SatException(_("Error in getUserConfigFile: missing user config file path"))
-        return self.user_config_file_path     
-        
-    
-def print_value(config, path, show_label, level=0, show_full_path=False):
-    '''Prints a value from the configuration. Prints recursively the values under the initial path.
-    
-    :param config class 'common.pyconf.Config': The configuration from which the value is displayed.
-    :param path str : the path in the configuration of the value to print.
-    :param show_label boolean: if True, do a basic display. (useful for bash completion)
-    :param level int: The number of spaces to add before display.
-    :param show_full_path :
-    '''            
-    
-    # display all the path or not
-    if show_full_path:
-        vname = path
-    else:
-        vname = path.split('.')[-1]
-
-    # number of spaces before the display
-    tab_level = "  " * level
-    
-    # call to the function that gets the value of the path.
-    try:
-        val = config.getByPath(path)
-    except Exception as e:
-        sys.stdout.write(tab_level)
-        sys.stdout.write("%s: ERROR %s\n" % (common.printcolors.printcLabel(vname), common.printcolors.printcError(str(e))))
-        return
-
-    # in this case, display only the value
-    if show_label:
-        sys.stdout.write(tab_level)
-        sys.stdout.write("%s: " % common.printcolors.printcLabel(vname))
-
-    # The case where the value has under values, do a recursive call to the function
-    if dir(val).__contains__('keys'):
-        if show_label: sys.stdout.write("\n")
-        for v in sorted(val.keys()):
-            print_value(config, path + '.' + v, show_label, level + 1)
-    elif val.__class__ == common.pyconf.Sequence or isinstance(val, list): # in this case, value is a list (or a Sequence)
-        if show_label: sys.stdout.write("\n")
-        index = 0
-        for v in val:
-            print_value(config, path + "[" + str(index) + "]", show_label, level + 1)
-            index = index + 1
-    else: # case where val is just a str
-        sys.stdout.write("%s\n" % val)
-
-def description():
-    '''method that is called when salomeTools is called with --help option.
-    
-    :return: The text to display for the config command description.
-    :rtype: str
-    '''
-    return _("The config command allows manipulation and operation on config files.")
-    
-
-def run(args, runner):
-    '''method that is called when salomeTools is called with config parameter.
-    '''
-    # Parse the options
-    (options, args) = parser.parse_args(args)
-    
-    # case : print a value of the config
-    if options.value:
-        if options.value == ".":
-            # if argument is ".", print all the config
-            for val in sorted(runner.cfg.keys()):
-                print_value(runner.cfg, val, True)
-            return
-        print_value(runner.cfg, options.value, True, level=0, show_full_path=False)
-        return
-    
-    # case : edit user pyconf file or application file
-    elif options.edit:
-        editor = runner.cfg.USER.editor
-        if 'APPLICATION' not in runner.cfg: # edit user pyconf
-            usercfg = os.path.join(runner.cfg.VARS.personalDir, 'salomeTools.pyconf')
-            common.system.show_in_editor(editor, usercfg)
-        else:
-            # search for file <application>.pyconf and open it
-            for path in runner.cfg.SITE.config.configPath:
-                pyconf_path = os.path.join(path, runner.cfg.VARS.application + ".pyconf")
-                if os.path.exists(pyconf_path):
-                    common.system.show_in_editor(editor, pyconf_path)
-                    break
-    
-    # case : copy an existing <application>.pyconf to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
-    elif options.copy:
-        # product is required
-        common.check_config_has_application( runner.cfg )
-
-        # get application file path 
-        source = runner.cfg.VARS.application + '.pyconf'
-        source_full_path = ""
-        for path in runner.cfg.SITE.config.configPath:
-            # ignore personal directory
-            if path == runner.cfg.VARS.personalDir:
-                continue
-            # loop on all directories that can have pyconf applications
-            zz = os.path.join(path, source)
-            if os.path.exists(zz):
-                source_full_path = zz
-                break
-
-        if len(source_full_path) == 0:
-            raise common.SatException(_("Config file for product %s not found\n") % source)
-        else:
-            if len(args) > 0:
-                # a name is given as parameter, use it
-                dest = args[0]
-            elif 'copy_prefix' in runner.cfg.SITE.config:
-                # use prefix
-                dest = runner.cfg.SITE.config.copy_prefix + runner.cfg.VARS.application
-            else:
-                # use same name as source
-                dest = runner.cfg.VARS.application
-                
-            # the full path
-            dest_file = os.path.join(runner.cfg.VARS.personalDir, 'Applications', dest + '.pyconf')
-            if os.path.exists(dest_file):
-                raise common.SatException(_("A personal application '%s' already exists") % dest)
-            
-            # perform the copy
-            shutil.copyfile(source_full_path, dest_file)
-            print(_("%s has been created.") % dest_file)
-    
-    # case : display all the available pyconf applications
-    elif options.list:
-        lproduct = list()
-        # search in all directories that can have pyconf applications
-        for path in runner.cfg.SITE.config.configPath:
-            # print a header
-            sys.stdout.write("------ %s\n" % common.printcolors.printcHeader(path))
-
-            if not os.path.exists(path):
-                sys.stdout.write(common.printcolors.printcError(_("Directory not found")) + "\n")
-            else:
-                for f in sorted(os.listdir(path)):
-                    # ignore file that does not ends with .pyconf
-                    if not f.endswith('.pyconf'):
-                        continue
-
-                    appliname = f[:-len('.pyconf')]
-                    if appliname not in lproduct:
-                        lproduct.append(appliname)
-                        if path.startswith(runner.cfg.VARS.personalDir):
-                            sys.stdout.write("%s*\n" % appliname)
-                        else:
-                            sys.stdout.write("%s\n" % appliname)
-
-            sys.stdout.write("\n")
-
-    
-    
\ No newline at end of file
diff --git a/src/i18n/build_strings.sh b/src/i18n/build_strings.sh
new file mode 100755 (executable)
index 0000000..9e2a95f
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# This script gets the strings to internationalise from the source code
+
+I18HOME=`dirname $0`
+SRC_DIR=$I18HOME/../..
+
+# get strings for french translation
+echo Build strings for French
+
+poFile=$I18HOME/fr/LC_MESSAGES/salomeTools.po
+refFile=$I18HOME/fr/LC_MESSAGES/ref.pot
+
+xgettext $SRC_DIR/*.py $SRC_DIR/common/*.py \
+    --no-wrap --no-location --language=Python --omit-header \
+    --output=$refFile
+msgmerge -q --update $poFile $refFile
+msgattrib --no-obsolete -o $poFile $poFile
+rm $refFile
+
diff --git a/src/i18n/fr/LC_MESSAGES/salomeTools.mo b/src/i18n/fr/LC_MESSAGES/salomeTools.mo
new file mode 100644 (file)
index 0000000..a63767d
Binary files /dev/null and b/src/i18n/fr/LC_MESSAGES/salomeTools.mo differ
diff --git a/src/i18n/fr/LC_MESSAGES/salomeTools.po b/src/i18n/fr/LC_MESSAGES/salomeTools.po
new file mode 100644 (file)
index 0000000..594e6c8
--- /dev/null
@@ -0,0 +1,41 @@
+msgid "shows global help or help on a specific command."
+msgstr "affiche l'aide gnrale ou pour une commande spcifique."
+
+msgid " is not a valid command"
+msgstr " n'est pas une commande valide"
+
+msgid "Usage: "
+msgstr "Utilisation : "
+
+msgid "Available commands are:\n"
+msgstr "Les commandes disponibles sont:\n"
+
+msgid ""
+"\n"
+"Getting the help for a specific command: "
+msgstr ""
+"\n"
+"Obtenir l'aide d'une commande spcifique : "
+
+msgid "lsb_release not installed\n"
+msgstr "lsb_release n'est pas install\n"
+
+msgid "You can define $LSB_PATH to give the path to lsb_release\n"
+msgstr "Vous pouvez dfinir $LSB_PATH pour donner le chemin vers lsb_release\n"
+
+#, python-format
+msgid "Unknown distribution: '%s'\n"
+msgstr "Distribution inconnue : '%s'\n"
+
+msgid "Please add your distribution to data/distrib.pyconf\n"
+msgstr "SVP ajoutez votre distribution au fichier data/distrib.pyconf\n"
+
+#, python-format
+msgid "Unknown architecture: '%s'\n"
+msgstr "Architecture inconnue: '%s'\n"
+
+msgid " is not a valid option"
+msgstr " n'est pas une option valide"
+
+msgid "Available options are:"
+msgstr "Les options disponibles sont:"
diff --git a/src/i18n/translate.py b/src/i18n/translate.py
new file mode 100644 (file)
index 0000000..a7a8171
--- /dev/null
@@ -0,0 +1,4 @@
+#/bin/env python
+import polib
+po = polib.pofile('fr/LC_MESSAGES/salomeTools.po', encoding='utf-8')
+po.save_as_mofile('fr/LC_MESSAGES/salomeTools.mo')
diff --git a/src/i18n/translate.sh b/src/i18n/translate.sh
new file mode 100755 (executable)
index 0000000..5ad2b2c
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# this script creates the binary resources file from the strings file
+
+I18HOME=`dirname $0`
+
+# french
+echo build French ressources
+msgfmt ${I18HOME}/fr/LC_MESSAGES/salomeTools.po -o ${I18HOME}/fr/LC_MESSAGES/salomeTools.mo
diff --git a/src/internal_config/distrib.pyconf b/src/internal_config/distrib.pyconf
new file mode 100644 (file)
index 0000000..18e6edd
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+# distribution codes
+# Code to use to replace the value returned by: lsb_release -si
+# If no code is found an error will be raised.
+DISTRIBUTIONS :
+{
+    "bullxServer": "BS"
+    "CentOS": "CO"
+    "Debian": "DB"
+    "Fedora": "FD"
+    "LinuxMint": "MN"
+    "Mageia": "MG"
+    "MandrivaLinux": "MD"
+    "RedHatEnterpriseServer": "RH"
+    "Ubuntu": "UB"
+    "openSUSE project":"OS"
+}
+
+# versions substitution
+# Code to use for release, to the replace value returned by: lsb_release -sr
+# If no code is found the value is used.
+VERSIONS :
+{
+    "DB":
+    {
+        "unstable": "sid"
+        "5.0": "05"
+        "4.0": "04"
+           "6.0.7" : "06"
+           "6.0.10": "06"
+           "7.1" : "07"
+           "7.5" : "07"
+    }
+    "MD":
+    {
+        "2008.0": "08"
+        "2010.0": "10"
+        "2010.1": "10"
+        "2010.2": "10"
+        "2007.1": "07"
+        "2006.0": "06"
+        "4" : "06"
+    }
+    "MG":
+    {
+        "3": "03"
+        "4": "04"
+        "5": "05"
+        "6": "06"
+    }
+    "CO":
+    {
+        "7.1.1503": "7.1"
+    }
+}
diff --git a/src/internal_config/salomeTools.pyconf b/src/internal_config/salomeTools.pyconf
new file mode 100644 (file)
index 0000000..9aa08bf
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+INTERNAL :
+{   
+    sat_version : "5.0.0dev"
+}
+
diff --git a/src/options.py b/src/options.py
new file mode 100644 (file)
index 0000000..b54b190
--- /dev/null
@@ -0,0 +1,174 @@
+#!/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
+'''The Options class that manages the access to all options passed as parameters in salomeTools command lines
+'''
+import getopt
+import sys
+from . import printcolors
+
+class OptResult(object):
+    '''An instance of this class will be the object manipulated in code of all salomeTools commands
+    The aim of this class is to have an elegant syntax to manipulate the options. 
+    ex: 
+    print(options.level)
+    5
+    '''
+    def __init__(self):
+        '''Initialization
+        '''
+        self.__dict__ = dict()
+
+    def __getattr__(self, name):
+        '''Overwrite of the __getattr__ function to customize it for option usage
+        
+        :param name str: The attribute to get the value.
+        :return: the value corresponding to the attribute.
+        :rtype: str,int,list,boolean
+        '''
+        if name in self.__dict__:
+            return self.__dict__[name]
+        else:
+            raise AttributeError(name + _(u" is not a valid option"))
+
+    def __setattr__(self, name, value):
+        '''Overwrite of the __setattr__ function to customize it for option usage
+        
+        :param name str: The attribute to set.
+        :param value str: The value  corresponding to the attribute.
+        :return: Nothing.
+        :rtype: N\A
+        '''
+        object.__setattr__(self,name,value)
+
+class Options:
+    '''Class to manage all salomeTools options
+    '''
+    def __init__(self):
+        '''Initialization
+        '''
+        # The options field stocks all options of a command in a list that contains dicts
+        self.options = []
+        # The list of available option type
+        self.availableOptions = ["boolean", "string", "int", "float", "long", "list", "list2"]
+
+    def add_option(self, shortName, longName, optionType, destName, helpString=""):
+        '''Method to add an option to a command. It gets all attributes of an option and append it in the options field
+        
+        :param shortName str: The short name of the option (ex "l" for level option).
+        :param longName str: The long name of the option (ex "level" for level option).
+        :param optionType str: The type of the option (ex "int").
+        :param destName str: The name that will be used in the code.
+        :param helpString str: The text to display when user ask for help on a command.     
+        :return: Nothing.
+        :rtype: N\A
+        '''
+        option = dict()
+        option['shortName'] = shortName
+        option['longName'] = longName
+
+        if optionType not in self.availableOptions:
+            print("error optionType", optionType, "not available.")
+            sys.exit(-1)
+
+        option['optionType'] = optionType
+        option['destName'] = destName
+        option['helpString'] = helpString
+        option['result'] = None
+        self.options.append(option)
+
+    def print_help(self):
+        '''Method that display all options stored in self.options and there help
+        
+        :return: Nothing.
+        :rtype: N\A
+        '''
+        # Do nothing if there are no options
+        if len(self.options) == 0:
+            return
+
+        # for all options, print its values. "shortname" is an optional field of the options 
+        print(printcolors.printcHeader(_("Available options are:")))
+        for option in self.options:
+            if 'shortName' in option and len(option['shortName']) > 0:
+                print(" -%(shortName)1s, --%(longName)s (%(optionType)s)\n\t%(helpString)s\n" % option)
+            else:
+                print(" --%(longName)s (%(optionType)s)\n\t%(helpString)s\n" % option)
+
+    def parse_args(self, argList=None):
+        '''Method that instantiates the class OptResult that gives access to all options in the code
+        
+        :param argList list: the raw list of arguments that were passed
+        :return: optResult, args : optResult is the option instance to manipulate in the code. args is the full raw list of passed options 
+        :rtype: (class 'common.options.OptResult',list)
+        '''
+        if argList is None:
+            argList = sys.argv[1:]
+        
+        # format shortNameOption and longNameOption to make right arguments to getopt.getopt function
+        shortNameOption = ""
+        longNameOption = []
+        for option in self.options:
+            shortNameOption = shortNameOption + option['shortName']
+            if option['shortName'] != "" and option['optionType'] != "boolean":
+                shortNameOption = shortNameOption + ":"
+
+            if option['longName'] != "":
+                if option['optionType'] != "boolean":
+                    longNameOption.append(option['longName'] + "=")
+                else:
+                    longNameOption.append(option['longName'])
+
+        # call to getopt.getopt function to get the option passed in the command regarding the available options
+        optlist, args = getopt.getopt(argList, shortNameOption, longNameOption)
+        
+        # instantiate and completing the optResult that will be returned
+        optResult = OptResult()
+        for option in self.options:
+            shortOption = "-" + option['shortName']
+            longOption = "--" + option['longName']
+            optionType = option['optionType']
+            for opt in optlist:
+                if opt[0] in [shortOption, longOption]:
+                    if optionType == "string":
+                        option['result'] = opt[1]
+                    elif optionType == "boolean":
+                        option['result'] = True
+                    elif optionType == "int":
+                        option['result'] = int(opt[1])
+                    elif optionType == "float":
+                        option['result'] = float(opt[1])
+                    elif optionType == "long":
+                        option['result'] = long(opt[1])
+                    elif optionType == "list":
+                        if option['result'] is None:
+                            option['result'] = list()
+                        option['result'].append(opt[1])
+                    elif optionType == "list2":
+                        if option['result'] is None:
+                            option['result'] = list()
+                        if opt[1].find(",") == -1:
+                            option['result'].append(opt[1])
+                        else:
+                            elts = filter(lambda l: len(l) > 0, opt[1].split(","))
+                            option['result'].extend(elts)
+
+            optResult.__setattr__(option['destName'], option['result'])
+            # free the option in order to be able to make a new free call of options (API case)
+            option['result'] = None
+        return optResult, args
+
diff --git a/src/printcolors.py b/src/printcolors.py
new file mode 100644 (file)
index 0000000..da76a47
--- /dev/null
@@ -0,0 +1,180 @@
+#!/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
+    '''
+    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("}")
+
+
diff --git a/src/pyconf.py b/src/pyconf.py
new file mode 100644 (file)
index 0000000..821f3d6
--- /dev/null
@@ -0,0 +1,1723 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that copyright notice and this permission notice appear in
+# supporting documentation, and that the name of Vinay Sajip
+# not be used in advertising or publicity pertaining to distribution
+# of the software without specific, written prior permission.
+# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#  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
+
+# CEA adds : 
+# Possibility to overwrites value in a pyconf file
+# Python 3 porting
+
+
+"""
+This is a configuration module for Python.
+
+This module should work under Python versions >= 2.2, and cannot be used with
+earlier versions since it uses new-style classes.
+
+Development and testing has only been carried out (so far) on Python 2.3.4 and
+Python 2.4.2. See the test module (test_config.py) included in the
+U{distribution<http://www.red-dove.com/python_config.html|_blank>} (follow the
+download link).
+
+A simple example - with the example configuration file::
+
+    messages:
+    [
+      {
+        stream : `sys.stderr`
+        message: 'Welcome'
+        name: 'Harry'
+      }
+      {
+        stream : `sys.stdout`
+        message: 'Welkom'
+        name: 'Ruud'
+      }
+      {
+        stream : $messages[0].stream
+        message: 'Bienvenue'
+        name: Yves
+      }
+    ]
+
+a program to read the configuration would be::
+
+    from config import Config
+
+    f = file('simple.cfg')
+    cfg = Config(f)
+    for m in cfg.messages:
+        s = '%s, %s' % (m.message, m.name)
+        try:
+            print >> m.stream, s
+        except IOError, e:
+            print e
+
+which, when run, would yield the console output::
+
+    Welcome, Harry
+    Welkom, Ruud
+    Bienvenue, Yves
+
+See U{this tutorial<http://www.red-dove.com/python_config.html|_blank>} for more
+information.
+
+#modified for salomeTools
+@version: 0.3.7.1
+
+@author: Vinay Sajip
+
+@copyright: Copyright (C) 2004-2007 Vinay Sajip. All Rights Reserved.
+
+
+@var streamOpener: The default stream opener. This is a factory function which
+takes a string (e.g. filename) and returns a stream suitable for reading. If
+unable to open the stream, an IOError exception should be thrown.
+
+The default value of this variable is L{defaultStreamOpener}. For an example
+of how it's used, see test_config.py (search for streamOpener).
+"""
+
+__author__  = "Vinay Sajip <vinay_sajip@red-dove.com>"
+__status__  = "alpha"
+__version__ = "0.3.7.1" #modified for salomeTools
+__date__    = "05 October 2007"
+
+import codecs
+import os
+import sys
+
+WORD = 'a'
+NUMBER = '9'
+STRING = '"'
+EOF = ''
+LCURLY = '{'
+RCURLY = '}'
+LBRACK = '['
+LBRACK2 = 'a['
+RBRACK = ']'
+LPAREN = '('
+LPAREN2 = '(('
+RPAREN = ')'
+DOT = '.'
+COMMA = ','
+COLON = ':'
+AT = '@'
+PLUS = '+'
+MINUS = '-'
+STAR = '*'
+SLASH = '/'
+MOD = '%'
+BACKTICK = '`'
+DOLLAR = '$'
+TRUE = 'True'
+FALSE = 'False'
+NONE = 'None'
+
+WORDCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
+
+if sys.platform == 'win32':
+    NEWLINE = '\r\n'
+elif os.name == 'mac':
+    NEWLINE = '\r'
+else:
+    NEWLINE = '\n'
+
+try:
+    has_utf32 = True
+except:
+    has_utf32 = False
+
+class ConfigInputStream(object):
+    """
+    An input stream which can read either ANSI files with default encoding
+    or Unicode files with BOMs.
+
+    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
+    built-in support.
+    """
+    def __init__(self, stream):
+        """
+        Initialize an instance.
+
+        @param stream: The underlying stream to be read. Should be seekable.
+        @type stream: A stream (file-like object).
+        """
+        encoding = None
+        signature = stream.read(4)
+        used = -1
+        if has_utf32:
+            if signature == codecs.BOM_UTF32_LE:
+                encoding = 'utf-32le'
+            elif signature == codecs.BOM_UTF32_BE:
+                encoding = 'utf-32be'
+        if encoding is None:
+            if signature[:3] == codecs.BOM_UTF8:
+                used = 3
+                encoding = 'utf-8'
+            elif signature[:2] == codecs.BOM_UTF16_LE:
+                used = 2
+                encoding = 'utf-16le'
+            elif signature[:2] == codecs.BOM_UTF16_BE:
+                used = 2
+                encoding = 'utf-16be'
+            else:
+                used = 0
+        if used >= 0:
+            stream.seek(used)
+        if encoding:
+            reader = codecs.getreader(encoding)
+            stream = reader(stream)
+        self.stream = stream
+        self.encoding = encoding
+
+    def read(self, size):
+        if (size == 0) or (self.encoding is None):
+            rv = self.stream.read(size)
+        else:
+            rv = u''
+            while size > 0:
+                rv += self.stream.read(1)
+                size -= 1
+        return rv
+
+    def close(self):
+        self.stream.close()
+
+    def readline(self):
+        if self.encoding is None:
+            line = ''
+        else:
+            line = u''
+        while True:
+            c = self.stream.read(1)
+            if isinstance(c, bytes):
+                c = c.decode()
+            if c:
+                line += c
+            if c == '\n':
+                break
+        return line
+
+class ConfigOutputStream(object):
+    """
+    An output stream which can write either ANSI files with default encoding
+    or Unicode files with BOMs.
+
+    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
+    built-in support.
+    """
+
+    def __init__(self, stream, encoding=None):
+        """
+        Initialize an instance.
+
+        @param stream: The underlying stream to be written.
+        @type stream: A stream (file-like object).
+        @param encoding: The desired encoding.
+        @type encoding: str
+        """
+        if encoding is not None:
+            encoding = str(encoding).lower()
+        self.encoding = encoding
+        if encoding == "utf-8":
+            stream.write(codecs.BOM_UTF8)
+        elif encoding == "utf-16be":
+            stream.write(codecs.BOM_UTF16_BE)
+        elif encoding == "utf-16le":
+            stream.write(codecs.BOM_UTF16_LE)
+        elif encoding == "utf-32be":
+            stream.write(codecs.BOM_UTF32_BE)
+        elif encoding == "utf-32le":
+            stream.write(codecs.BOM_UTF32_LE)
+
+        if encoding is not None:
+            writer = codecs.getwriter(encoding)
+            stream = writer(stream)
+        self.stream = stream
+
+    def write(self, data):
+        self.stream.write(data)
+
+    def flush(self):
+        self.stream.flush()
+
+    def close(self):
+        self.stream.close()
+
+def defaultStreamOpener(name):
+    """
+    This function returns a read-only stream, given its name. The name passed
+    in should correspond to an existing stream, otherwise an exception will be
+    raised.
+
+    This is the default value of L{streamOpener}; assign your own callable to
+    streamOpener to return streams based on names. For example, you could use
+    urllib2.urlopen().
+
+    @param name: The name of a stream, most commonly a file name.
+    @type name: str
+    @return: A stream with the specified name.
+    @rtype: A read-only stream (file-like object)
+    """
+    return ConfigInputStream(open(name, 'rb'))
+
+streamOpener = None
+
+__resolveOverwrite__ = True
+
+class ConfigError(Exception):
+    """
+    This is the base class of exceptions raised by this module.
+    """
+    pass
+
+class ConfigFormatError(ConfigError):
+    """
+    This is the base class of exceptions raised due to syntax errors in
+    configurations.
+    """
+    pass
+
+class ConfigResolutionError(ConfigError):
+    """
+    This is the base class of exceptions raised due to semantic errors in
+    configurations.
+    """
+    pass
+
+def isWord(s):
+    """
+    See if a passed-in value is an identifier. If the value passed in is not a
+    string, False is returned. An identifier consists of alphanumerics or
+    underscore characters.
+
+    Examples::
+
+        isWord('a word') ->False
+        isWord('award') -> True
+        isWord(9) -> False
+        isWord('a_b_c_') ->True
+
+    @note: isWord('9abc') will return True - not exactly correct, but adequate
+    for the way it's used here.
+
+    @param s: The name to be tested
+    @type s: any
+    @return: True if a word, else False
+    @rtype: bool
+    """
+    if type(s) != type(''):
+        return False
+    s = s.replace('_', '')
+    return s.isalnum()
+
+def makePath(prefix, suffix):
+    """
+    Make a path from a prefix and suffix.
+
+    Examples::
+
+        makePath('', 'suffix') -> 'suffix'
+        makePath('prefix', 'suffix') -> 'prefix.suffix'
+        makePath('prefix', '[1]') -> 'prefix[1]'
+
+    @param prefix:  The prefix to use. If it evaluates as false, the suffix
+                    is returned.
+    @type prefix:   str
+    @param suffix:  The suffix to use. It is either an identifier or an
+                    index in brackets.
+    @type suffix:   str
+    @return:        The path concatenation of prefix and suffix, with a
+                    dot if the suffix is not a bracketed index.
+    @rtype:         str
+
+    """
+    if not prefix:
+        rv = suffix
+    elif suffix[0] == '[':
+        rv = prefix + suffix
+    else:
+        rv = prefix + '.' + suffix
+    return rv
+
+
+class Container(object):
+    """
+    This internal class is the base class for mappings and sequences.
+
+    @ivar path: A string which describes how to get
+    to this instance from the root of the hierarchy.
+
+    Example::
+
+        a.list.of[1].or['more'].elements
+    """
+    def __init__(self, parent):
+        """
+        Initialize an instance.
+
+        @param parent: The parent of this instance in the hierarchy.
+        @type parent: A L{Container} instance.
+        """
+        object.__setattr__(self, 'parent', parent)
+
+    def setPath(self, path):
+        """
+        Set the path for this instance.
+        @param path: The path - a string which describes how to get
+        to this instance from the root of the hierarchy.
+        @type path: str
+        """
+        object.__setattr__(self, 'path', path)
+
+    def evaluate(self, item):
+        """
+        Evaluate items which are instances of L{Reference} or L{Expression}.
+
+        L{Reference} instances are evaluated using L{Reference.resolve},
+        and L{Expression} instances are evaluated using
+        L{Expression.evaluate}.
+
+        @param item: The item to be evaluated.
+        @type item: any
+        @return: If the item is an instance of L{Reference} or L{Expression},
+        the evaluated value is returned, otherwise the item is returned
+        unchanged.
+        """
+        if isinstance(item, Reference):
+            item = item.resolve(self)
+        elif isinstance(item, Expression):
+            item = item.evaluate(self)
+        return item
+
+    def writeToStream(self, stream, indent, container):
+        """
+        Write this instance to a stream at the specified indentation level.
+
+        Should be redefined in subclasses.
+
+        @param stream: The stream to write to
+        @type stream: A writable stream (file-like object)
+        @param indent: The indentation level
+        @type indent: int
+        @param container: The container of this instance
+        @type container: L{Container}
+        @raise NotImplementedError: If a subclass does not override this
+        """
+        raise NotImplementedError
+
+    def writeValue(self, value, stream, indent):
+        if isinstance(self, Mapping):
+            indstr = ' '
+        else:
+            indstr = indent * '  '
+        if isinstance(value, Reference) or isinstance(value, Expression):
+            stream.write('%s%r%s' % (indstr, value, NEWLINE))
+        else:
+            if isinstance(value, str): # and not isWord(value):
+                value = repr(value)
+            stream.write('%s%s%s' % (indstr, value, NEWLINE))
+
+class Mapping(Container):
+    """
+    This internal class implements key-value mappings in configurations.
+    """
+
+    def __init__(self, parent=None):
+        """
+        Initialize an instance.
+
+        @param parent: The parent of this instance in the hierarchy.
+        @type parent: A L{Container} instance.
+        """
+        Container.__init__(self, parent)
+        object.__setattr__(self, 'path', '')
+        object.__setattr__(self, 'data', {})
+        object.__setattr__(self, 'order', [])   # to preserve ordering
+        object.__setattr__(self, 'comments', {})
+
+    def __delitem__(self, key):
+        """
+        Remove an item
+        """
+        data = object.__getattribute__(self, 'data')
+        if key not in data:
+            raise AttributeError(key)
+        order = object.__getattribute__(self, 'order')
+        comments = object.__getattribute__(self, 'comments')
+        del data[key]
+        order.remove(key)
+        del comments[key]
+
+    def __getitem__(self, key):
+        data = object.__getattribute__(self, 'data')
+        if key not in data:
+            raise AttributeError(key)
+        rv = data[key]
+        return self.evaluate(rv)
+
+    __getattr__ = __getitem__
+    
+    '''
+    def __getattribute__(self, name):
+        if name == "__dict__":
+            return {}
+        if name in ["__methods__", "__members__"]:
+            return []
+        #if name == "__class__":
+        #    return ''
+        data = object.__getattribute__(self, "data")
+        useData = data.has_key(name)
+        if useData:
+            rv = getattr(data, name)
+        else:
+            rv = object.__getattribute__(self, name)
+            if rv is None:
+                raise AttributeError(name)
+        return rv
+    '''
+
+    def iteritems(self):
+        for key in self.keys():
+            yield(key, self[key])
+        raise StopIteration
+
+    def __contains__(self, item):
+        order = object.__getattribute__(self, 'order')
+        return item in order
+
+    def addMapping(self, key, value, comment, setting=False):
+        """
+        Add a key-value mapping with a comment.
+
+        @param key: The key for the mapping.
+        @type key: str
+        @param value: The value for the mapping.
+        @type value: any
+        @param comment: The comment for the key (can be None).
+        @type comment: str
+        @param setting: If True, ignore clashes. This is set
+        to true when called from L{__setattr__}.
+        @raise ConfigFormatError: If an existing key is seen
+        again and setting is False.
+        """
+        data = object.__getattribute__(self, 'data')
+        order = object.__getattribute__(self, 'order')
+        comments = object.__getattribute__(self, 'comments')
+
+        data[key] = value
+        if key not in order:
+            order.append(key)
+        elif not setting:
+            raise ConfigFormatError("repeated key: %s" % key)
+        comments[key] = comment
+
+    def __setattr__(self, name, value):
+        self.addMapping(name, value, None, True)
+
+    __setitem__ = __setattr__
+
+    def keys(self):
+        """
+        Return the keys in a similar way to a dictionary.
+        """
+        return object.__getattribute__(self, 'order')
+
+    def get(self, key, default=None):
+        """
+        Allows a dictionary-style get operation.
+        """
+        if key in self:
+            return self[key]
+        return default
+
+    def __str__(self):
+        return str(object.__getattribute__(self, 'data'))
+
+    def __repr__(self):
+        return repr(object.__getattribute__(self, 'data'))
+
+    def __len__(self):
+        return len(object.__getattribute__(self, 'order'))
+
+    def __iter__(self):
+        return self.iterkeys()
+
+    def iterkeys(self):
+        order = object.__getattribute__(self, 'order')
+        return order.__iter__()
+
+    def writeToStream(self, stream, indent, container):
+        """
+        Write this instance to a stream at the specified indentation level.
+
+        Should be redefined in subclasses.
+
+        @param stream: The stream to write to
+        @type stream: A writable stream (file-like object)
+        @param indent: The indentation level
+        @type indent: int
+        @param container: The container of this instance
+        @type container: L{Container}
+        """
+        indstr = indent * '  '
+        if len(self) == 0:
+            stream.write(' { }%s' % NEWLINE)
+        else:
+            if isinstance(container, Mapping):
+                stream.write(NEWLINE)
+            stream.write('%s{%s' % (indstr, NEWLINE))
+            self.__save__(stream, indent + 1)
+            stream.write('%s}%s' % (indstr, NEWLINE))
+
+    def __save__(self, stream, indent=0):
+        """
+        Save this configuration to the specified stream.
+        @param stream: A stream to which the configuration is written.
+        @type stream: A write-only stream (file-like object).
+        @param indent: The indentation level for the output.
+        @type indent: int
+        """
+        indstr = indent * '  '
+        order = object.__getattribute__(self, 'order')
+        data = object.__getattribute__(self, 'data')
+        maxlen = 0 # max(map(lambda x: len(x), order))
+        for key in order:
+            comment = self.comments[key]
+            if isWord(key):
+                skey = key
+            else:
+                skey = repr(key)
+            if comment:
+                stream.write('%s#%s' % (indstr, comment))
+            stream.write('%s%-*s :' % (indstr, maxlen, skey))
+            value = data[key]
+            if isinstance(value, Container):
+                value.writeToStream(stream, indent, self)
+            else:
+                self.writeValue(value, stream, indent)
+
+class Config(Mapping):
+    """
+    This class represents a configuration, and is the only one which clients
+    need to interface to, under normal circumstances.
+    """
+
+    class Namespace(object):
+        """
+        This internal class is used for implementing default namespaces.
+
+        An instance acts as a namespace.
+        """
+        def __init__(self):
+            self.sys = sys
+            self.os = os
+
+    def __init__(self, streamOrFile=None, parent=None):
+        """
+        Initializes an instance.
+
+        @param streamOrFile: If specified, causes this instance to be loaded
+        from the stream (by calling L{load}). If a string is provided, it is
+        passed to L{streamOpener} to open a stream. Otherwise, the passed
+        value is assumed to be a stream and used as is.
+        @type streamOrFile: A readable stream (file-like object) or a name.
+        @param parent: If specified, this becomes the parent of this instance
+        in the configuration hierarchy.
+        @type parent: a L{Container} instance.
+        """
+        Mapping.__init__(self, parent)
+        object.__setattr__(self, 'reader', ConfigReader(self))
+        object.__setattr__(self, 'namespaces', [Config.Namespace()])
+        if streamOrFile is not None:
+            if isinstance(streamOrFile, str) or isinstance(streamOrFile, bytes):
+                global streamOpener
+                if streamOpener is None:
+                    streamOpener = defaultStreamOpener
+                streamOrFile = streamOpener(streamOrFile)
+            load = object.__getattribute__(self, "load")
+            load(streamOrFile)
+
+    def load(self, stream):
+        """
+        Load the configuration from the specified stream. Multiple streams can
+        be used to populate the same instance, as long as there are no
+        clashing keys. The stream is closed.
+        @param stream: A stream from which the configuration is read.
+        @type stream: A read-only stream (file-like object).
+        @raise ConfigError: if keys in the loaded configuration clash with
+        existing keys.
+        @raise ConfigFormatError: if there is a syntax error in the stream.
+        """
+        reader = object.__getattribute__(self, 'reader')
+        reader.load(stream)
+        stream.close()
+
+    def addNamespace(self, ns, name=None):
+        """
+        Add a namespace to this configuration which can be used to evaluate
+        (resolve) dotted-identifier expressions.
+        @param ns: The namespace to be added.
+        @type ns: A module or other namespace suitable for passing as an
+        argument to vars().
+        @param name: A name for the namespace, which, if specified, provides
+        an additional level of indirection.
+        @type name: str
+        """
+        namespaces = object.__getattribute__(self, 'namespaces')
+        if name is None:
+            namespaces.append(ns)
+        else:
+            setattr(namespaces[0], name, ns)
+
+    def removeNamespace(self, ns, name=None):
+        """
+        Remove a namespace added with L{addNamespace}.
+        @param ns: The namespace to be removed.
+        @param name: The name which was specified when L{addNamespace} was
+        called.
+        @type name: str
+        """
+        namespaces = object.__getattribute__(self, 'namespaces')
+        if name is None:
+            namespaces.remove(ns)
+        else:
+            delattr(namespaces[0], name)
+
+    def __save__(self, stream, indent=0, no_close=False):
+        """
+        Save this configuration to the specified stream. The stream is
+        closed if this is the top-level configuration in the hierarchy.
+        L{Mapping.__save__} is called to do all the work.
+        @param stream: A stream to which the configuration is written.
+        @type stream: A write-only stream (file-like object).
+        @param indent: The indentation level for the output.
+        @type indent: int
+        """
+        Mapping.__save__(self, stream, indent)
+        if indent == 0 and not no_close:
+            stream.close()
+
+    def getByPath(self, path):
+        """
+        Obtain a value in the configuration via its path.
+        @param path: The path of the required value
+        @type path: str
+        @return the value at the specified path.
+        @rtype: any
+        @raise ConfigError: If the path is invalid
+        """
+        s = 'self.' + path
+        try:
+            return eval(s)
+        except Exception as e:
+            raise ConfigError(str(e))
+
+class Sequence(Container):
+    """
+    This internal class implements a value which is a sequence of other values.
+    """
+    class SeqIter(object):
+        """
+        This internal class implements an iterator for a L{Sequence} instance.
+        """
+        def __init__(self, seq):
+            self.seq = seq
+            self.limit = len(object.__getattribute__(seq, 'data'))
+            self.index = 0
+
+        def __iter__(self):
+            return self
+
+        def next(self):
+            if self.index >= self.limit:
+                raise StopIteration
+            rv = self.seq[self.index]
+            self.index += 1
+            return rv
+        
+        # This method is for python3 compatibility
+        def __next__(self): 
+            if self.index >= self.limit:
+                raise StopIteration
+            rv = self.seq[self.index]
+            self.index += 1
+            return rv
+
+    def __init__(self, parent=None):
+        """
+        Initialize an instance.
+
+        @param parent: The parent of this instance in the hierarchy.
+        @type parent: A L{Container} instance.
+        """
+        Container.__init__(self, parent)
+        object.__setattr__(self, 'data', [])
+        object.__setattr__(self, 'comments', [])
+
+    def append(self, item, comment):
+        """
+        Add an item to the sequence.
+
+        @param item: The item to add.
+        @type item: any
+        @param comment: A comment for the item.
+        @type comment: str
+        """
+        data = object.__getattribute__(self, 'data')
+        comments = object.__getattribute__(self, 'comments')
+        data.append(item)
+        comments.append(comment)
+
+    def __getitem__(self, index):
+        data = object.__getattribute__(self, 'data')
+        try:
+            rv = data[index]
+        except (IndexError, KeyError, TypeError):
+            raise ConfigResolutionError('%r is not a valid index for %r' % (index, object.__getattribute__(self, 'path')))
+        if not isinstance(rv, list):
+            rv = self.evaluate(rv)
+        else:
+            # deal with a slice
+            result = []
+            for a in rv:
+                result.append(self.evaluate(a))
+            rv = result
+        return rv
+
+    def __iter__(self):
+        return Sequence.SeqIter(self)
+
+    def __repr__(self):
+        return repr(object.__getattribute__(self, 'data'))
+
+    def __str__(self):
+        return str(self[:]) # using the slice evaluates the contents
+
+    def __len__(self):
+        return len(object.__getattribute__(self, 'data'))
+
+    def writeToStream(self, stream, indent, container):
+        """
+        Write this instance to a stream at the specified indentation level.
+
+        Should be redefined in subclasses.
+
+        @param stream: The stream to write to
+        @type stream: A writable stream (file-like object)
+        @param indent: The indentation level
+        @type indent: int
+        @param container: The container of this instance
+        @type container: L{Container}
+        """
+        indstr = indent * '  '
+        if len(self) == 0:
+            stream.write(' [ ]%s' % NEWLINE)
+        else:
+            if isinstance(container, Mapping):
+                stream.write(NEWLINE)
+            stream.write('%s[%s' % (indstr, NEWLINE))
+            self.__save__(stream, indent + 1)
+            stream.write('%s]%s' % (indstr, NEWLINE))
+
+    def __save__(self, stream, indent):
+        """
+        Save this instance to the specified stream.
+        @param stream: A stream to which the configuration is written.
+        @type stream: A write-only stream (file-like object).
+        @param indent: The indentation level for the output, > 0
+        @type indent: int
+        """
+        if indent == 0:
+            raise ConfigError("sequence cannot be saved as a top-level item")
+        data = object.__getattribute__(self, 'data')
+        comments = object.__getattribute__(self, 'comments')
+        indstr = indent * '  '
+        for i in xrange(0, len(data)):
+            value = data[i]
+            comment = comments[i]
+            if comment:
+                stream.write('%s#%s' % (indstr, comment))
+            if isinstance(value, Container):
+                value.writeToStream(stream, indent, self)
+            else:
+                self.writeValue(value, stream, indent)
+
+class Reference(object):
+    """
+    This internal class implements a value which is a reference to another value.
+    """
+    def __init__(self, config, type, ident):
+        """
+        Initialize an instance.
+
+        @param config: The configuration which contains this reference.
+        @type config: A L{Config} instance.
+        @param type: The type of reference.
+        @type type: BACKTICK or DOLLAR
+        @param ident: The identifier which starts the reference.
+        @type ident: str
+        """
+        self.config = config
+        self.type = type
+        self.elements = [ident]
+
+    def addElement(self, type, ident):
+        """
+        Add an element to the reference.
+
+        @param type: The type of reference.
+        @type type: BACKTICK or DOLLAR
+        @param ident: The identifier which continues the reference.
+        @type ident: str
+        """
+        self.elements.append((type, ident))
+
+    def findConfig(self, container):
+        """
+        Find the closest enclosing configuration to the specified container.
+
+        @param container: The container to start from.
+        @type container: L{Container}
+        @return: The closest enclosing configuration, or None.
+        @rtype: L{Config}
+        """
+        while (container is not None) and not isinstance(container, Config):
+            container = object.__getattribute__(container, 'parent')
+        return container
+
+    def resolve(self, container):
+        """
+        Resolve this instance in the context of a container.
+
+        @param container: The container to resolve from.
+        @type container: L{Container}
+        @return: The resolved value.
+        @rtype: any
+        @raise ConfigResolutionError: If resolution fails.
+        """
+        rv = None
+        path = object.__getattribute__(container, 'path')
+        current = container
+        while current is not None:
+            if self.type == BACKTICK:
+                namespaces = object.__getattribute__(current, 'namespaces')
+                found = False
+                for ns in namespaces:
+                    try:
+                        rv = eval(str(self)[1:-1], vars(ns))
+                        found = True
+                        break
+                    except:
+                        pass
+                if found:
+                    break
+            else:
+                key = self.elements[0]
+                try:
+                    rv = current[key]
+                    for item in self.elements[1:]:
+                        key = item[1]
+                        rv = rv[key]
+                    break
+                except:
+                    rv = None
+                    pass
+            current = object.__getattribute__(current, 'parent')
+        if current is None:
+            raise ConfigResolutionError("unable to evaluate %r in the configuration %s" % (self, path))
+        return rv
+
+    def __str__(self):
+        s = self.elements[0]
+        for tt, tv in self.elements[1:]:
+            if tt == DOT:
+                s += '.%s' % tv
+            else:
+                s += '[%r]' % tv
+        if self.type == BACKTICK:
+            return BACKTICK + s + BACKTICK
+        else:
+            return DOLLAR + s
+
+    def __repr__(self):
+        return self.__str__()
+
+class Expression(object):
+    """
+    This internal class implements a value which is obtained by evaluating an expression.
+    """
+    def __init__(self, op, lhs, rhs):
+        """
+        Initialize an instance.
+
+        @param op: the operation expressed in the expression.
+        @type op: PLUS, MINUS, STAR, SLASH, MOD
+        @param lhs: the left-hand-side operand of the expression.
+        @type lhs: any Expression or primary value.
+        @param rhs: the right-hand-side operand of the expression.
+        @type rhs: any Expression or primary value.
+        """
+        self.op = op
+        self.lhs = lhs
+        self.rhs = rhs
+
+    def __str__(self):
+        return '%r %s %r' % (self.lhs, self.op, self.rhs)
+
+    def __repr__(self):
+        return self.__str__()
+
+    def evaluate(self, container):
+        """
+        Evaluate this instance in the context of a container.
+
+        @param container: The container to evaluate in from.
+        @type container: L{Container}
+        @return: The evaluated value.
+        @rtype: any
+        @raise ConfigResolutionError: If evaluation fails.
+        @raise ZeroDivideError: If division by zero occurs.
+        @raise TypeError: If the operation is invalid, e.g.
+        subtracting one string from another.
+        """
+        lhs = self.lhs
+        if isinstance(lhs, Reference):
+            lhs = lhs.resolve(container)
+        elif isinstance(lhs, Expression):
+            lhs = lhs.evaluate(container)
+        rhs = self.rhs
+        if isinstance(rhs, Reference):
+            rhs = rhs.resolve(container)
+        elif isinstance(rhs, Expression):
+            rhs = rhs.evaluate(container)
+        op = self.op
+        if op == PLUS:
+            rv = lhs + rhs
+        elif op == MINUS:
+            rv = lhs - rhs
+        elif op == STAR:
+            rv = lhs * rhs
+        elif op == SLASH:
+            rv = lhs / rhs
+        else:
+            rv = lhs % rhs
+        return rv
+
+class ConfigReader(object):
+    """
+    This internal class implements a parser for configurations.
+    """
+
+    def __init__(self, config):
+        self.filename = None
+        self.config = config
+        self.lineno = 0
+        self.colno = 0
+        self.lastc = None
+        self.last_token = None
+        self.commentchars = '#'
+        self.whitespace = ' \t\r\n'
+        self.quotes = '\'"'
+        self.punct = ':-+*/%,.{}[]()@`$'
+        self.digits = '0123456789'
+        self.wordchars = '%s' % WORDCHARS # make a copy
+        self.identchars = self.wordchars + self.digits
+        self.pbchars = []
+        self.pbtokens = []
+        self.comment = None
+
+    def location(self):
+        """
+        Return the current location (filename, line, column) in the stream
+        as a string.
+
+        Used when printing error messages,
+
+        @return: A string representing a location in the stream being read.
+        @rtype: str
+        """
+        return "%s(%d,%d)" % (self.filename, self.lineno, self.colno)
+
+    def getChar(self):
+        """
+        Get the next char from the stream. Update line and column numbers
+        appropriately.
+
+        @return: The next character from the stream.
+        @rtype: str
+        """
+        if self.pbchars:
+            c = self.pbchars.pop()
+            if isinstance(c,bytes):
+                c = c.decode()
+        else:
+            c = self.stream.read(1)
+            if isinstance(c,bytes):
+                try:
+                    c = c.decode()
+                except:
+                    import pdb;pdb.set_trace()
+            self.colno += 1
+            if c == '\n':
+                self.lineno += 1
+                self.colno = 1
+        return c
+
+    def __repr__(self):
+        return "<ConfigReader at 0x%08x>" % id(self)
+
+    __str__ = __repr__
+
+    def getToken(self):
+        """
+        Get a token from the stream. String values are returned in a form
+        where you need to eval() the returned value to get the actual
+        string. The return value is (token_type, token_value).
+
+        Multiline string tokenizing is thanks to David Janes (BlogMatrix)
+
+        @return: The next token.
+        @rtype: A token tuple.
+        """
+        if self.pbtokens:
+            return self.pbtokens.pop()
+        stream = self.stream
+        self.comment = None
+        token = ''
+        tt = EOF
+        while True:
+            c = self.getChar()
+            if not c:
+                break
+            elif c == '#':
+                if self.comment :
+                    self.comment += '#' + stream.readline()
+                else :
+                    self.comment = stream.readline()
+                self.lineno += 1
+                continue
+            if c in self.quotes:
+                token = c
+                quote = c
+                tt = STRING
+                escaped = False
+                multiline = False
+                c1 = self.getChar()
+                if c1 == quote:
+                    c2 = self.getChar()
+                    if c2 == quote:
+                        multiline = True
+                        token += quote
+                        token += quote
+                    else:
+                        self.pbchars.append(c2)
+                        self.pbchars.append(c1)
+                else:
+                    self.pbchars.append(c1)
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    token += c
+                    if (c == quote) and not escaped:
+                        if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
+                            break
+                    if c == '\\':
+                        escaped = not escaped
+                    else:
+                        escaped = False
+                if not c:
+                    raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
+                break
+            if c in self.whitespace:
+                self.lastc = c
+                continue
+            elif c in self.punct:
+                token = c
+                tt = c
+                if (self.lastc == ']') or (self.lastc in self.identchars):
+                    if c == '[':
+                        tt = LBRACK2
+                    elif c == '(':
+                        tt = LPAREN2
+                break
+            elif c in self.digits:
+                token = c
+                tt = NUMBER
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    if c in self.digits:
+                        token += c
+                    elif (c == '.') and token.find('.') < 0:
+                        token += c
+                    else:
+                        if c and (c not in self.whitespace):
+                            self.pbchars.append(c)
+                        break
+                break
+            elif c in self.wordchars:
+                token = c
+                tt = WORD
+                c = self.getChar()
+                while c and (c in self.identchars):
+                    token += c
+                    c = self.getChar()
+                if c: # and c not in self.whitespace:
+                    self.pbchars.append(c)
+                if token == "True":
+                    tt = TRUE
+                elif token == "False":
+                    tt = FALSE
+                elif token == "None":
+                    tt = NONE
+                break
+            else:
+                raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
+        if token:
+            self.lastc = token[-1]
+        else:
+            self.lastc = None
+        self.last_token = tt
+        return (tt, token)
+
+    def load(self, stream, parent=None, suffix=None):
+        """
+        Load the configuration from the specified stream.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        @param parent: The parent of the configuration (to which this reader
+        belongs) in the hierarchy. Specified when the configuration is
+        included in another one.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix of this configuration in the parent
+        configuration. Should be specified whenever the parent is not None.
+        @raise ConfigError: If parent is specified but suffix is not.
+        @raise ConfigFormatError: If there are syntax errors in the stream.
+        """
+        if parent is not None:
+            if suffix is None:
+                raise ConfigError("internal error: load called with parent but no suffix")
+            self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.setStream(stream)
+        self.token = self.getToken()
+        self.parseMappingBody(self.config)
+        if self.token[0] != EOF:
+            raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
+
+    def setStream(self, stream):
+        """
+        Set the stream to the specified value, and prepare to read from it.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        """
+        self.stream = stream
+        if hasattr(stream, 'name'):
+            filename = stream.name
+        else:
+            filename = '?'
+        self.filename = filename
+        self.lineno = 1
+        self.colno = 1
+
+    def match(self, t):
+        """
+        Ensure that the current token type matches the specified value, and
+        advance to the next token.
+
+        @param t: The token type to match.
+        @type t: A valid token type.
+        @return: The token which was last read from the stream before this
+        function is called.
+        @rtype: a token tuple - see L{getToken}.
+        @raise ConfigFormatError: If the token does not match what's expected.
+        """
+        if self.token[0] != t:
+            raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
+        rv = self.token
+        self.token = self.getToken()
+        return rv
+
+    def parseMappingBody(self, parent):
+        """
+        Parse the internals of a mapping, and add entries to the provided
+        L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        """
+        while self.token[0] in [WORD, STRING]:
+            self.parseKeyValuePair(parent)
+
+    def parseKeyValuePair(self, parent):
+        """
+        Parse a key-value pair, and add it to the provided L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        comment = self.comment
+        tt, tv = self.token
+        if tt == WORD:
+            key = tv
+            suffix = tv
+        elif tt == STRING:
+            key = eval(tv)
+            suffix = '[%s]' % tv
+        else:
+            msg = "%s: expecting word or string, found %r"
+            raise ConfigFormatError(msg % (self.location(), tv))
+        self.token = self.getToken()
+        # for now, we allow key on its own as a short form of key : True
+        if self.token[0] == COLON:
+            self.token = self.getToken()
+            value = self.parseValue(parent, suffix)
+        else:
+            value = True
+        try:
+            parent.addMapping(key, value, comment)
+        except Exception as e:
+            raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
+                                    self.token[1]))
+        tt = self.token[0]
+        if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
+            msg = "%s: expecting one of EOF, WORD, STRING, \
+RCURLY, COMMA, found %r"
+            raise ConfigFormatError(msg  % (self.location(), self.token[1]))
+        if tt == COMMA:
+            self.token = self.getToken()
+
+    def parseValue(self, parent, suffix):
+        """
+        Parse a value.
+
+        @param parent: The container to which the value will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: The value
+        @rtype: any
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
+                  TRUE, FALSE, NONE, BACKTICK, MINUS]:
+            rv = self.parseScalar()
+        elif tt == LBRACK:
+            rv = self.parseSequence(parent, suffix)
+        elif tt in [LCURLY, AT]:
+            rv = self.parseMapping(parent, suffix)
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseSequence(self, parent, suffix):
+        """
+        Parse a sequence.
+
+        @param parent: The container to which the sequence will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Sequence} instance representing the sequence.
+        @rtype: L{Sequence}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        rv = Sequence(parent)
+        rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.match(LBRACK)
+        comment = self.comment
+        tt = self.token[0]
+        while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
+                     TRUE, FALSE, NONE, BACKTICK]:
+            suffix = '[%d]' % len(rv)
+            value = self.parseValue(parent, suffix)
+            rv.append(value, comment)
+            tt = self.token[0]
+            comment = self.comment
+            if tt == COMMA:
+                self.match(COMMA)
+                tt = self.token[0]
+                comment = self.comment
+                continue
+        self.match(RBRACK)
+        return rv
+
+    def parseMapping(self, parent, suffix):
+        """
+        Parse a mapping.
+
+        @param parent: The container to which the mapping will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Mapping} instance representing the mapping.
+        @rtype: L{Mapping}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        if self.token[0] == LCURLY:
+            self.match(LCURLY)
+            rv = Mapping(parent)
+            rv.setPath(
+               makePath(object.__getattribute__(parent, 'path'), suffix))
+            self.parseMappingBody(rv)
+            self.match(RCURLY)
+        else:
+            self.match(AT)
+            _, fn = self.match(STRING)
+            rv = Config(eval(fn), parent)
+        return rv
+
+    def parseScalar(self):
+        """
+        Parse a scalar - a terminal value such as a string or number, or
+        an L{Expression} or L{Reference}.
+
+        @return: the parsed scalar
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseTerm()
+        tt = self.token[0]
+        while tt in [PLUS, MINUS]:
+            self.match(tt)
+            rhs = self.parseTerm()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseTerm(self):
+        """
+        Parse a term in an additive expression (a + b, a - b)
+
+        @return: the parsed term
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseFactor()
+        tt = self.token[0]
+        while tt in [STAR, SLASH, MOD]:
+            self.match(tt)
+            rhs = self.parseFactor()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseFactor(self):
+        """
+        Parse a factor in an multiplicative expression (a * b, a / b, a % b)
+
+        @return: the parsed factor
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
+            rv = self.token[1]
+            if tt != WORD:
+                rv = eval(rv)
+            self.match(tt)
+        elif tt == LPAREN:
+            self.match(LPAREN)
+            rv = self.parseScalar()
+            self.match(RPAREN)
+        elif tt == DOLLAR:
+            self.match(DOLLAR)
+            rv = self.parseReference(DOLLAR)
+        elif tt == BACKTICK:
+            self.match(BACKTICK)
+            rv = self.parseReference(BACKTICK)
+            self.match(BACKTICK)
+        elif tt == MINUS:
+            self.match(MINUS)
+            rv = -self.parseScalar()
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseReference(self, type):
+        """
+        Parse a reference.
+
+        @return: the parsed reference
+        @rtype: L{Reference}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        word = self.match(WORD)
+        rv = Reference(self.config, type, word[1])
+        while self.token[0] in [DOT, LBRACK2]:
+            self.parseSuffix(rv)
+        return rv
+
+    def parseSuffix(self, ref):
+        """
+        Parse a reference suffix.
+
+        @param ref: The reference of which this suffix is a part.
+        @type ref: L{Reference}.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt == DOT:
+            self.match(DOT)
+            word = self.match(WORD)
+            ref.addElement(DOT, word[1])
+        else:
+            self.match(LBRACK2)
+            tt, tv = self.token
+            if tt not in [NUMBER, STRING]:
+                raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
+            self.token = self.getToken()
+            tv = eval(tv)
+            self.match(RBRACK)
+            ref.addElement(LBRACK, tv)
+
+def defaultMergeResolve(map1, map2, key):
+    """
+    A default resolver for merge conflicts. Returns a string
+    indicating what action to take to resolve the conflict.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    @return: One of "merge", "append", "mismatch" or "overwrite"
+             indicating what action should be taken. This should
+             be appropriate to the objects being merged - e.g.
+             there is no point returning "merge" if the two objects
+             are instances of L{Sequence}.
+    @rtype: str
+    """
+    obj1 = map1[key]
+    obj2 = map2[key]
+    if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
+        rv = "merge"
+    elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
+        rv = "append"
+    else:
+        rv = "mismatch"
+    return rv
+
+def overwriteMergeResolve(map1, map2, key):
+    """
+    An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
+    but where a "mismatch" is detected, returns "overwrite" instead.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    """
+    rv = defaultMergeResolve(map1, map2, key)
+    if rv == "mismatch":
+        rv = "overwrite"
+    return rv
+
+class ConfigMerger(object):
+    """
+    This class is used for merging two configurations. If a key exists in the
+    merge operand but not the merge target, then the entry is copied from the
+    merge operand to the merge target. If a key exists in both configurations,
+    then a resolver (a callable) is called to decide how to handle the
+    conflict.
+    """
+
+    def __init__(self, resolver=defaultMergeResolve):
+        """
+        Initialise an instance.
+
+        @param resolver:
+        @type resolver: A callable which takes the argument list
+        (map1, map2, key) where map1 is the mapping being merged into,
+        map2 is the merge operand and key is the clashing key. The callable
+        should return a string indicating how the conflict should be resolved.
+        For possible return values, see L{defaultMergeResolve}. The default
+        value preserves the old behaviour
+        """
+        self.resolver = resolver
+
+    def merge(self, merged, mergee):
+        """
+        Merge two configurations. The second configuration is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param merged: The configuration to merge into.
+        @type merged: L{Config}.
+        @param mergee: The configuration to merge.
+        @type mergee: L{Config}.
+        """
+        self.mergeMapping(merged, mergee)
+
+    def overwriteKeys(self, map1, seq2):
+        """
+        Renint variables. The second mapping is unchanged,
+        and the first is changed depending the keys of the second mapping.
+        @param map1: The mapping to reinit keys into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping container reinit information.
+        @type map2: L{Mapping}.
+        """
+
+        overwrite_list = object.__getattribute__(seq2, 'data')
+        for overwrite_instruction in overwrite_list:
+            object.__setattr__(overwrite_instruction, 'parent', map1)
+            if "__condition__" in overwrite_instruction.keys():
+                overwrite_condition = overwrite_instruction["__condition__"]
+                if eval(overwrite_condition, globals(), map1):
+                    for key in overwrite_instruction.keys():
+                        if key == "__condition__":
+                            continue
+                        try:
+                            exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
+                        except:
+                            exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+            else:
+                for key in overwrite_instruction.keys():
+                    try:
+                        exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
+                    except:
+                        exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+
+    def mergeMapping(self, map1, map2):
+        """
+        Merge two mappings recursively. The second mapping is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param map1: The mapping to merge into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping to merge.
+        @type map2: L{Mapping}.
+        """
+        keys = map1.keys()
+        global __resolveOverwrite__
+        for key in map2.keys():
+            if __resolveOverwrite__ and key == "__overwrite__":
+                self.overwriteKeys(map1,map2[key])
+
+            elif key not in keys:
+                map1[key] = map2[key]
+                if isinstance(map1[key], Container) :
+                    object.__setattr__(map1[key], 'parent', map1)
+            else:
+                obj1 = map1[key]
+                obj2 = map2[key]
+                decision = self.resolver(map1, map2, key)
+                if decision == "merge":
+                    self.mergeMapping(obj1, obj2)
+                elif decision == "append":
+                    self.mergeSequence(obj1, obj2)
+                elif decision == "overwrite":
+                    map1[key] = obj2
+                    if isinstance(map1[key], Container):
+                        object.__setattr__(map1[key], 'parent', map1)
+                elif decision == "mismatch":
+                    self.handleMismatch(obj1, obj2)
+                else:
+                    msg = "unable to merge: don't know how to implement %r"
+                    raise ValueError(msg % decision)
+
+    def mergeSequence(self, seq1, seq2):
+        """
+        Merge two sequences. The second sequence is unchanged,
+        and the first is changed to have the elements of the second
+        appended to it.
+
+        @param seq1: The sequence to merge into.
+        @type seq1: L{Sequence}.
+        @param seq2: The sequence to merge.
+        @type seq2: L{Sequence}.
+        """
+        data1 = object.__getattribute__(seq1, 'data')
+        data2 = object.__getattribute__(seq2, 'data')
+        for obj in data2:
+            data1.append(obj)
+        comment1 = object.__getattribute__(seq1, 'comments')
+        comment2 = object.__getattribute__(seq2, 'comments')
+        for obj in comment2:
+            comment1.append(obj)
+
+    def handleMismatch(self, obj1, obj2):
+        """
+        Handle a mismatch between two objects.
+
+        @param obj1: The object to merge into.
+        @type obj1: any
+        @param obj2: The object to merge.
+        @type obj2: any
+        """
+        raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
+
+class ConfigList(list):
+    """
+    This class implements an ordered list of configurations and allows you
+    to try getting the configuration from each entry in turn, returning
+    the first successfully obtained value.
+    """
+
+    def getByPath(self, path):
+        """
+        Obtain a value from the first configuration in the list which defines
+        it.
+
+        @param path: The path of the value to retrieve.
+        @type path: str
+        @return: The value from the earliest configuration in the list which
+        defines it.
+        @rtype: any
+        @raise ConfigError: If no configuration in the list has an entry with
+        the specified path.
+        """
+        found = False
+        rv = None
+        for entry in self:
+            try:
+                rv = entry.getByPath(path)
+                found = True
+                break
+            except ConfigError:
+                pass
+        if not found:
+            raise ConfigError("unable to resolve %r" % path)
+        return rv
diff --git a/src/salomeTools.py b/src/salomeTools.py
deleted file mode 100755 (executable)
index 014c9ee..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-'''This file is the main entry file to salomeTools
-'''
-
-# python imports
-import os
-import sys
-import imp
-import types
-import gettext
-
-# salomeTools imports
-import common.options
-import config
-
-# get path to salomeTools sources
-srcdir = os.path.dirname(os.path.realpath(__file__))
-
-# load resources for internationalization
-#es = gettext.translation('salomeTools', os.path.join(srcdir, 'common', 'i18n'))
-#es.install()
-gettext.install('salomeTools', os.path.join(srcdir, 'common', 'i18n'))
-
-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.endswith('.py') and item!='salomeTools.py' and item!='__init__.py':
-            cmd_list.append(item[:-len('.py')])
-    return cmd_list
-
-# The list of valid salomeTools commands
-#lCommand = ['config', 'compile', 'prepare']
-lCommand = find_command_list(srcdir)
-
-# Define all possible option for salomeTools command :  sat <option> <args>
-parser = common.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."))
-
-class salomeTools(object):
-    '''The main class that stores all the commands of salomeTools
-    '''
-    def __init__(self, opt='', dataDir=None):
-        '''Initialization
-        
-        :param opt str: The sat options 
-        :param: dataDir str : the directory that contain all the external data (like software pyconf and software scripts)
-        '''
-        # Read the salomeTools options (the list of possible options is at the beginning of this file)
-        try:
-            (options, argus) = parser.parse_args(opt.split(' '))
-        except Exception as exc:
-            write_exception(exc)
-            sys.exit(-1)
-
-        # initialization of class attributes       
-        self.__dict__ = dict()
-        self.cfg = None # the config that will be read using pyconf module
-        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__(srcdir)
-        
-        # if the help option has been called, print help and exit
-        if options.help:
-            try:
-                self.print_help(argus)
-                sys.exit(0)
-            except Exception as exc:
-                write_exception(exc)
-                sys.exit(1)
-
-    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(name + _(" is not a valid command"))
-    
-    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:
-            # 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=''):
-                '''The function that will load the configuration (all pyconf)
-                and return the function run of the command corresponding to module
-                
-                :param args str: The directory path containing the commands 
-                '''
-                argv = args.split(" ")
-                
-                # if it is provided by the command line, get the application
-                appliToLoad = None
-                if argv != [''] and argv[0][0] != "-":
-                    appliToLoad = argv[0].rstrip('*')
-                    argv = argv[1:]
-                
-                # Read the config if it is not already done
-                if not self.cfg:
-                    # read the configuration from all the pyconf files    
-                    cfgManager = config.ConfigManager()
-                    self.cfg = cfgManager.getConfig(dataDir=self.dataDir, application=appliToLoad, options=self.options, command=__nameCmd__)
-                
-                return __module__.run(argv, self)
-
-            # 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 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 = config.ConfigManager()
-        self.cfg = cfgManager.getConfig(dataDir=self.dataDir)
-
-        # Check if this command exists
-        if not hasattr(self, command):
-            raise common.SatException(_("Command '%s' does not exist") % command)
-        
-        # Print salomeTools version
-        print_version()
-        
-        # load the module
-        module = self.get_module(command)
-
-        # print the description of the command that is done in the command file
-        if hasattr( module, "description" ) :
-            print(common.printcolors.printcHeader( _("Description:") ))
-            print(module.description() + '\n')
-
-        # print the description of the command options
-        if hasattr( module, "parser" ) :
-            module.parser.print_help()
-
-    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 common.SatException(_("Command '%s' does not exist") % module)
-
-        # load the module
-        (file_, pathname, description) = imp.find_module(module, [srcdir])
-        module = imp.load_module(module, file_, pathname, description)
-        return module
-def print_version():
-    '''prints salomeTools version (in src/common/internal_config/salomeTools.pyconf)
-    '''
-    # read the config 
-    cfgManager = config.ConfigManager()
-    cfg = cfgManager.getConfig()
-    # print the key corresponding to salomeTools version
-    print(common.printcolors.printcHeader( _("Version: ") ) + cfg.INTERNAL.sat_version + '\n')
-
-
-def print_help():
-    '''prints salomeTools general help
-    
-    :param options str: the options
-    '''
-    print_version()
-    
-    print(common.printcolors.printcHeader( _("Usage: ") ) + "sat [sat_options] <command> [product] [command_options]\n")
-
-    parser.print_help()
-
-    # display all the available commands.
-    print(common.printcolors.printcHeader(_("Available commands are:\n")))
-    for command in lCommand:
-        print(" - %s" % (command))
-        
-    # Explain how to get the help for a specific command
-    print(common.printcolors.printcHeader(_("\nGetting the help for a specific command: ")) + "sat --help <command>\n")
-
-def write_exception(exc):
-    '''write exception in case of error in a command
-    
-    :param exc exception: the exception to print
-    '''
-    sys.stderr.write("\n***** ")
-    sys.stderr.write(common.printcolors.printcError("salomeTools ERROR:"))
-    sys.stderr.write("\n" + str(exc) + "\n")
-
-# ###############################
-# MAIN : terminal command usage #
-# ###############################
-if __name__ == "__main__":  
-    # Get the command line using sys.argv
-    cmd_line = " ".join(sys.argv)
-    # Initialize the code that will be returned by the terminal command 
-    code = 0
-    (options, args) = parser.parse_args(sys.argv[1:])
-    
-    # no arguments : print general help
-    if len(args) == 0:
-        print_help()
-        sys.exit(0)
-    
-    # instantiate the salomeTools class with correct options
-    sat = salomeTools(' '.join(sys.argv[1:]))
-    # the command called
-    command = args[0]
-    # get dynamically the command function to call
-    fun_command = sat.__getattr__(command)
-    # call the command with two cases : mode debug or not
-    if options.debug_mode:
-        # call classically the command and if it fails, show exception and stack (usual python mode)
-        code = fun_command(' '.join(args[1:]))
-    else:
-        # catch exception in order to show less verbose but elegant message
-        try:
-            code = fun_command(' '.join(args[1:]))
-        except Exception as exc:
-            code = 1
-            write_exception(exc)
-    
-    # exit salomeTools with the right code (0 if no errors, else 1)
-    if code is None: code = 0
-    sys.exit(code)
-        
\ No newline at end of file
diff --git a/src/system.py b/src/system.py
new file mode 100644 (file)
index 0000000..cc8a39f
--- /dev/null
@@ -0,0 +1,47 @@
+#!/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 : all functions that do a system call, like open a browser or an editor, or call a git command
+'''
+
+import sys
+import subprocess
+
+
+def show_in_editor(editor, filePath):
+    '''open filePath using editor.
+    
+    :param editor str: The editor to use.
+    :param filePath str: The path to the file to open.
+    '''
+    # default editor is vi
+    if editor is None or len(editor) == 0:
+        editor = 'vi'
+    
+    if '%s' not in editor:
+        editor += ' %s'
+
+    try:
+        # launch cmd using subprocess.Popen
+        cmd = editor % filePath
+        p = subprocess.Popen(cmd, shell=True)
+        p.communicate()
+    except:
+        sys.stderr.write("Unable to edit file %s\n" % filePath)
+    
\ No newline at end of file
diff --git a/src/test_command.py b/src/test_command.py
deleted file mode 100644 (file)
index cac7729..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-import common
-
-# Define all possible option for config command :  sat config <options>
-parser = common.options.Options()
-parser.add_option('u', 'unique', 'boolean', 'unique', "TEST d'option.")
-parser.add_option('v', 'value', 'string', 'value', "Appelle la commande config avec l'option value.")
-
-def description():
-    return _("Test d'une commande supplémentaire.")
-    
-
-def run(args, runner):
-    (options, args) = parser.parse_args(args)
-    if options.unique:
-        print('unique')
-    elif options.value:
-        runner.cfg.VARS.user = 'TEST'
-        runner.config('-v ' + options.value)
index bf43f67d915458e14bfd83da55b5e5bbd4265c58..5b1e19a595def6904aa0a319bf00adc53d1b633d 100644 (file)
@@ -23,10 +23,10 @@ import shutil
 
 # get execution path
 testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+sys.path.append(os.path.join(testdir, '..', '..'))
+sys.path.append(os.path.join(testdir, '..', '_testTools'))
 
-from salomeTools import salomeTools
+from salomeTools import Sat
 import HTMLTestRunner
 
 class TestConfig(unittest.TestCase):
@@ -43,7 +43,7 @@ class TestConfig(unittest.TestCase):
             shutil.move(user_dir, user_dir_save)
                
         # The command to test
-        sat = salomeTools('')
+        sat = Sat('')
         sat.config('-v .')
 
         expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'salomeTools.pyconf'))
index 4e20f3a3844c129e0d1769ca77dc8877cf4e9177..4f709949ee764beca8f95c0e7ff83e67bfa60d85 100644 (file)
@@ -23,10 +23,10 @@ import shutil
 
 # get execution path
 testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+sys.path.append(os.path.join(testdir, '..', '..'))
+sys.path.append(os.path.join(testdir, '..', '_testTools'))
 
-from salomeTools import salomeTools
+from salomeTools import Sat
 import HTMLTestRunner
 
 class TestConfig(unittest.TestCase):
@@ -44,7 +44,7 @@ class TestConfig(unittest.TestCase):
             os.remove(expected_file)
                
         # The command to test
-        sat = salomeTools('')
+        sat = Sat('')
         sat.config('appli-test -c')
 
 
index f54c0dd9558da19ad931764f843e2e583c17fb5e..3ea8a37e372d49afef8234b4c2ae71aa6395d17b 100644 (file)
@@ -23,10 +23,10 @@ import platform
 
 # get execution path
 testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+sys.path.append(os.path.join(testdir, '..', '..'))
+sys.path.append(os.path.join(testdir, '..', '_testTools'))
 
-from salomeTools import salomeTools
+from salomeTools import Sat
 from tools import outRedirection
 import HTMLTestRunner
 
@@ -44,7 +44,7 @@ class TestConfig(unittest.TestCase):
         my_out = outRedirection()
 
         # The command to test
-        sat = salomeTools('')
+        sat = Sat()
         sat.config('-v VARS.hostname')
 
         # stop output redirection
@@ -69,7 +69,7 @@ class TestConfig(unittest.TestCase):
         my_out = outRedirection()
 
         # The command to test
-        sat = salomeTools('')
+        sat = Sat()
         sat.config('-l')
 
         # stop output redirection
index e123f2d02b2b85dc429a04e06fb6bce4118a47e9..20d80eb81ec4232d8c6b7e019da692ba3047ec36 100644 (file)
@@ -23,10 +23,10 @@ import platform
 
 # get execution path
 testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+sys.path.append(os.path.join(testdir, '..', '..'))
+sys.path.append(os.path.join(testdir, '..', '_testTools'))
 
-from salomeTools import salomeTools
+from salomeTools import Sat
 from tools import outRedirection
 import HTMLTestRunner
 
@@ -44,7 +44,7 @@ class TestConfig(unittest.TestCase):
         my_out = outRedirection()
 
         # The command to test
-        sat = salomeTools('')
+        sat = Sat('')
         sat.config('-v VARS.python')
 
         # stop output redirection
index 84f863258b62c42a6d0f2840f5189872a09417dd..a6efebb43be7a4316962481d66afd04b0cd97537 100755 (executable)
@@ -16,8 +16,8 @@
 #  License along with this library; if not, write to the Free Software
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-coverage run --source=../src/config.py    config/option_value.py > test_res.html
-coverage run --source=../src/config.py -a config/option_value_2.py >> test_res.html
-coverage run --source=../src/config.py -a config/create_user_pyconf.py >> test_res.html
-coverage run --source=../src/config.py -a config/option_copy.py >> test_res.html
+coverage run --source=../commands/config.py    config/option_value.py > test_res.html
+coverage run --source=../commands/config.py -a config/option_value_2.py >> test_res.html
+coverage run --source=../commands/config.py -a config/create_user_pyconf.py >> test_res.html
+coverage run --source=../commands/config.py -a config/option_copy.py >> test_res.html
 coverage html
index 5c52d4b4a7f852afdef208bf1715605f5a38bd24..6c767d3cc2dba7f43ba4e5c43de4cb20603b0b4a 100644 (file)
@@ -21,12 +21,12 @@ import sys
 
 # get execution path
 testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', 'src'))
+sys.path.append(os.path.join(testdir, '..'))
 
-from salomeTools import salomeTools
+from salomeTools import Sat
 
-sat = salomeTools('')
+sat = Sat()
 sat.config('appli-test -v APPLICATION')
 
-sat2 = salomeTools("-oINTERNAL.sat_version='coucou'")
+sat2 = Sat("-oINTERNAL.sat_version='coucou'")
 sat2.config('appli-test -v INTERNAL.sat_version')