--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+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
--- /dev/null
+#!/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)
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+'''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
-src/salomeTools.py
\ No newline at end of file
+salomeTools.py
\ No newline at end of file
+#!/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
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2013 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+'''
+In this file : 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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-'''
-In this file : 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
+++ /dev/null
-#!/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
-
+++ /dev/null
-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:"
+++ /dev/null
-#/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')
+++ /dev/null
-#!/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
-
+++ /dev/null
-#!/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"
- }
-}
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-INTERNAL :
-{
- sat_version : "5.0.0dev"
-}
-
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-'''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
-
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-'''In this file is stored the mechanism that manage color prints in the terminal
-'''
-
-# define constant to use in scripts
-COLOR_ERROR = 'ERROR'
-COLOR_WARNING = 'WARNING'
-COLOR_SUCCESS = 'SUCCESS'
-COLOR_LABEL = 'LABEL'
-COLOR_HEADER = 'HEADER'
-COLOR_INFO = 'INFO'
-COLOR_HIGLIGHT = 'HIGHLIGHT'
-
-# the color map to use to print the colors
-__colormap__ = {
- COLOR_ERROR: '\033[1m\033[31m',
- COLOR_SUCCESS: '\033[1m\033[32m',
- COLOR_WARNING: '\033[33m',
- COLOR_HEADER: '\033[34m',
- COLOR_INFO: '\033[35m',
- COLOR_LABEL: '\033[36m',
- COLOR_HIGLIGHT: '\033[97m\033[43m'
-}
-
-# list of available codes
-__code_range__ = [1, 4] + list(range(30, 38)) + list(range(40, 48)) + list(range(90, 98)) + list(range(100, 108))
-
-def printc(txt, code=''):
- '''print a text with colors
-
- :param txt str: The text to be printed.
- :param code str: The color to use.
- :return: The colored text.
- :rtype: str
- '''
- # no code means 'auto mode' (works only for OK, KO, NO and ERR*)
- if code == '':
- striptxt = txt.strip().upper()
- if striptxt == "OK":
- code = COLOR_SUCCESS
- elif striptxt in ["KO", "NO"] or striptxt.startswith("ERR"):
- code = COLOR_ERROR
- else:
- return txt
-
- # no code => output the originial text
- if code not in __colormap__.keys() or __colormap__[code] == '':
- return txt
-
- return __colormap__[code] + txt + '\033[0m'
-
-def printcInfo(txt):
- '''print a text info color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_INFO)
-
-def printcError(txt):
- '''print a text error color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_ERROR)
-
-def printcWarning(txt):
- '''print a text warning color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_WARNING)
-
-def printcHeader(txt):
- '''print a text header color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_HEADER)
-
-def printcLabel(txt):
- '''print a text label color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_LABEL)
-
-def printcSuccess(txt):
- '''print a text success color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_SUCCESS)
-
-def printcHighlight(txt):
- '''print a text highlight color
-
- :param txt str: The text to be printed.
- :return: The colored text.
- :rtype: str
- '''
- return printc(txt, COLOR_HIGLIGHT)
-
-def cleancolor(message):
- '''remove color from a colored text.
-
- :param message str: The text to be cleaned.
- :return: The cleaned text.
- :rtype: str
- '''
- 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("}")
-
-
+++ /dev/null
-#!/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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-'''
-In this file : 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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-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
--- /dev/null
+#!/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
+
--- /dev/null
+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:"
--- /dev/null
+#/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')
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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"
+ }
+}
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+INTERNAL :
+{
+ sat_version : "5.0.0dev"
+}
+
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2013 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''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
+
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2013 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''In this file is stored the mechanism that manage color prints in the terminal
+'''
+
+# define constant to use in scripts
+COLOR_ERROR = 'ERROR'
+COLOR_WARNING = 'WARNING'
+COLOR_SUCCESS = 'SUCCESS'
+COLOR_LABEL = 'LABEL'
+COLOR_HEADER = 'HEADER'
+COLOR_INFO = 'INFO'
+COLOR_HIGLIGHT = 'HIGHLIGHT'
+
+# the color map to use to print the colors
+__colormap__ = {
+ COLOR_ERROR: '\033[1m\033[31m',
+ COLOR_SUCCESS: '\033[1m\033[32m',
+ COLOR_WARNING: '\033[33m',
+ COLOR_HEADER: '\033[34m',
+ COLOR_INFO: '\033[35m',
+ COLOR_LABEL: '\033[36m',
+ COLOR_HIGLIGHT: '\033[97m\033[43m'
+}
+
+# list of available codes
+__code_range__ = [1, 4] + list(range(30, 38)) + list(range(40, 48)) + list(range(90, 98)) + list(range(100, 108))
+
+def printc(txt, code=''):
+ '''print a text with colors
+
+ :param txt str: The text to be printed.
+ :param code str: The color to use.
+ :return: The colored text.
+ :rtype: str
+ '''
+ # no code means 'auto mode' (works only for OK, KO, NO and ERR*)
+ if code == '':
+ striptxt = txt.strip().upper()
+ if striptxt == "OK":
+ code = COLOR_SUCCESS
+ elif striptxt in ["KO", "NO"] or striptxt.startswith("ERR"):
+ code = COLOR_ERROR
+ else:
+ return txt
+
+ # no code => output the originial text
+ if code not in __colormap__.keys() or __colormap__[code] == '':
+ return txt
+
+ return __colormap__[code] + txt + '\033[0m'
+
+def printcInfo(txt):
+ '''print a text info color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_INFO)
+
+def printcError(txt):
+ '''print a text error color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_ERROR)
+
+def printcWarning(txt):
+ '''print a text warning color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_WARNING)
+
+def printcHeader(txt):
+ '''print a text header color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_HEADER)
+
+def printcLabel(txt):
+ '''print a text label color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_LABEL)
+
+def printcSuccess(txt):
+ '''print a text success color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_SUCCESS)
+
+def printcHighlight(txt):
+ '''print a text highlight color
+
+ :param txt str: The text to be printed.
+ :return: The colored text.
+ :rtype: str
+ '''
+ return printc(txt, COLOR_HIGLIGHT)
+
+def cleancolor(message):
+ '''remove color from a colored text.
+
+ :param message str: The text to be cleaned.
+ :return: The cleaned text.
+ :rtype: str
+ '''
+ 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("}")
+
+
--- /dev/null
+#!/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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-'''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
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2013 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+'''
+In this file : 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
+++ /dev/null
-#!/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)
# 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):
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'))
# 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):
os.remove(expected_file)
# The command to test
- sat = salomeTools('')
+ sat = Sat('')
sat.config('appli-test -c')
# 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
my_out = outRedirection()
# The command to test
- sat = salomeTools('')
+ sat = Sat()
sat.config('-v VARS.hostname')
# stop output redirection
my_out = outRedirection()
# The command to test
- sat = salomeTools('')
+ sat = Sat()
sat.config('-l')
# stop output redirection
# 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
my_out = outRedirection()
# The command to test
- sat = salomeTools('')
+ sat = Sat('')
sat.config('-v VARS.python')
# stop output redirection
# 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
# 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')