Salome HOME
salomeTools-5 FIRST COMMIT
authorSerge Rehbinder <serge.rehbinder@cea.fr>
Tue, 26 Jan 2016 14:11:58 +0000 (15:11 +0100)
committerSerge Rehbinder <serge.rehbinder@cea.fr>
Tue, 26 Jan 2016 14:11:58 +0000 (15:11 +0100)
data/distrib.pyconf [new file with mode: 0644]
sat [new symlink]
src/common/__init__.py [new file with mode: 0644]
src/common/architecture.py [new file with mode: 0644]
src/common/config_pyconf.py [new file with mode: 0644]
src/common/options.py [new file with mode: 0644]
src/common/printcolors.py [new file with mode: 0755]
src/config.py [new file with mode: 0644]
src/salomeTools.py [new file with mode: 0755]

diff --git a/data/distrib.pyconf b/data/distrib.pyconf
new file mode 100644 (file)
index 0000000..18e6edd
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+# distribution codes
+# Code to use to replace the value returned by: lsb_release -si
+# If no code is found an error will be raised.
+DISTRIBUTIONS :
+{
+    "bullxServer": "BS"
+    "CentOS": "CO"
+    "Debian": "DB"
+    "Fedora": "FD"
+    "LinuxMint": "MN"
+    "Mageia": "MG"
+    "MandrivaLinux": "MD"
+    "RedHatEnterpriseServer": "RH"
+    "Ubuntu": "UB"
+    "openSUSE project":"OS"
+}
+
+# versions substitution
+# Code to use for release, to the replace value returned by: lsb_release -sr
+# If no code is found the value is used.
+VERSIONS :
+{
+    "DB":
+    {
+        "unstable": "sid"
+        "5.0": "05"
+        "4.0": "04"
+           "6.0.7" : "06"
+           "6.0.10": "06"
+           "7.1" : "07"
+           "7.5" : "07"
+    }
+    "MD":
+    {
+        "2008.0": "08"
+        "2010.0": "10"
+        "2010.1": "10"
+        "2010.2": "10"
+        "2007.1": "07"
+        "2006.0": "06"
+        "4" : "06"
+    }
+    "MG":
+    {
+        "3": "03"
+        "4": "04"
+        "5": "05"
+        "6": "06"
+    }
+    "CO":
+    {
+        "7.1.1503": "7.1"
+    }
+}
diff --git a/sat b/sat
new file mode 120000 (symlink)
index 0000000..804194a
--- /dev/null
+++ b/sat
@@ -0,0 +1 @@
+src/salomeTools.py
\ No newline at end of file
diff --git a/src/common/__init__.py b/src/common/__init__.py
new file mode 100644 (file)
index 0000000..e6ddc37
--- /dev/null
@@ -0,0 +1,23 @@
+#!/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
+
+
+from . import config_pyconf
+from . import architecture
+from . import printcolors
+from . import options
\ No newline at end of file
diff --git a/src/common/architecture.py b/src/common/architecture.py
new file mode 100644 (file)
index 0000000..241e50f
--- /dev/null
@@ -0,0 +1,144 @@
+#!/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(_("lsb_release not installed\n"))
+        sys.stderr.write(_("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(_("Unknown distribution: '%s'\n") % distrib)
+        sys.stderr.write(_("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_nb_bit():
+    '''Gets the number of bytes.
+    :return: the number of bytes of the OS on which salomeTools is running.
+    :rtype: str
+    '''
+
+    # The platform python module gives the answer
+    nb_bit = platform.architecture()[0]
+    if nb_bit == "64bit":
+        nb_bit = "64"
+    elif nb_bit == "32bit":
+        nb_bit = "32"
+    else:
+        sys.stderr.write(_("Unknown architecture: '%s'\n") % nb_bit)
+        sys.exit(-1)
+
+    return nb_bit
+
+def get_python_version():
+    '''Gets the version of the running python.
+    :return: the version of the running python.
+    :rtype: str
+    '''
+    
+    # The platform python module gives the answer
+    return platform.python_version()
+
+def get_nb_proc():
+    '''Gets the number of processors of the machine on which salomeTools is running.
+    :return: the number of processors.
+    :rtype: str
+    '''
+    
+    try :
+        import multiprocessing
+        nb_proc=multiprocessing.cpu_count()
+    except :
+        nb_proc=int(os.sysconf('SC_NPROCESSORS_ONLN'))
+    return nb_proc
\ No newline at end of file
diff --git a/src/common/config_pyconf.py b/src/common/config_pyconf.py
new file mode 100644 (file)
index 0000000..89b66bd
--- /dev/null
@@ -0,0 +1,1693 @@
+#!/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.
+
+"""
+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, unicode):
+                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
+
+    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):
+                c = c.decode()
+            self.colno += 1
+            if c == '\n':
+                self.lineno += 1
+                self.colno = 1
+        return c
+
+    def __repr__(self):
+        return "<ConfigReader at 0x%08x>" % id(self)
+
+    __str__ = __repr__
+
+    def getToken(self):
+        """
+        Get a token from the stream. String values are returned in a form
+        where you need to eval() the returned value to get the actual
+        string. The return value is (token_type, token_value).
+
+        Multiline string tokenizing is thanks to David Janes (BlogMatrix)
+
+        @return: The next token.
+        @rtype: A token tuple.
+        """
+        if self.pbtokens:
+            return self.pbtokens.pop()
+        stream = self.stream
+        self.comment = None
+        token = ''
+        tt = EOF
+        while True:
+            c = self.getChar()
+            if not c:
+                break
+            elif c == '#':
+                if self.comment :
+                    self.comment += '#' + stream.readline()
+                else :
+                    self.comment = stream.readline()
+                self.lineno += 1
+                continue
+            if c in self.quotes:
+                token = c
+                quote = c
+                tt = STRING
+                escaped = False
+                multiline = False
+                c1 = self.getChar()
+                if c1 == quote:
+                    c2 = self.getChar()
+                    if c2 == quote:
+                        multiline = True
+                        token += quote
+                        token += quote
+                    else:
+                        self.pbchars.append(c2)
+                        self.pbchars.append(c1)
+                else:
+                    self.pbchars.append(c1)
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    token += c
+                    if (c == quote) and not escaped:
+                        if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
+                            break
+                    if c == '\\':
+                        escaped = not escaped
+                    else:
+                        escaped = False
+                if not c:
+                    raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
+                break
+            if c in self.whitespace:
+                self.lastc = c
+                continue
+            elif c in self.punct:
+                token = c
+                tt = c
+                if (self.lastc == ']') or (self.lastc in self.identchars):
+                    if c == '[':
+                        tt = LBRACK2
+                    elif c == '(':
+                        tt = LPAREN2
+                break
+            elif c in self.digits:
+                token = c
+                tt = NUMBER
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    if c in self.digits:
+                        token += c
+                    elif (c == '.') and token.find('.') < 0:
+                        token += c
+                    else:
+                        if c and (c not in self.whitespace):
+                            self.pbchars.append(c)
+                        break
+                break
+            elif c in self.wordchars:
+                token = c
+                tt = WORD
+                c = self.getChar()
+                while c and (c in self.identchars):
+                    token += c
+                    c = self.getChar()
+                if c: # and c not in self.whitespace:
+                    self.pbchars.append(c)
+                if token == "True":
+                    tt = TRUE
+                elif token == "False":
+                    tt = FALSE
+                elif token == "None":
+                    tt = NONE
+                break
+            else:
+                raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
+        if token:
+            self.lastc = token[-1]
+        else:
+            self.lastc = None
+        self.last_token = tt
+        return (tt, token)
+
+    def load(self, stream, parent=None, suffix=None):
+        """
+        Load the configuration from the specified stream.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        @param parent: The parent of the configuration (to which this reader
+        belongs) in the hierarchy. Specified when the configuration is
+        included in another one.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix of this configuration in the parent
+        configuration. Should be specified whenever the parent is not None.
+        @raise ConfigError: If parent is specified but suffix is not.
+        @raise ConfigFormatError: If there are syntax errors in the stream.
+        """
+        if parent is not None:
+            if suffix is None:
+                raise ConfigError("internal error: load called with parent but no suffix")
+            self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.setStream(stream)
+        self.token = self.getToken()
+        self.parseMappingBody(self.config)
+        if self.token[0] != EOF:
+            raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
+
+    def setStream(self, stream):
+        """
+        Set the stream to the specified value, and prepare to read from it.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        """
+        self.stream = stream
+        if hasattr(stream, 'name'):
+            filename = stream.name
+        else:
+            filename = '?'
+        self.filename = filename
+        self.lineno = 1
+        self.colno = 1
+
+    def match(self, t):
+        """
+        Ensure that the current token type matches the specified value, and
+        advance to the next token.
+
+        @param t: The token type to match.
+        @type t: A valid token type.
+        @return: The token which was last read from the stream before this
+        function is called.
+        @rtype: a token tuple - see L{getToken}.
+        @raise ConfigFormatError: If the token does not match what's expected.
+        """
+        if self.token[0] != t:
+            raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
+        rv = self.token
+        self.token = self.getToken()
+        return rv
+
+    def parseMappingBody(self, parent):
+        """
+        Parse the internals of a mapping, and add entries to the provided
+        L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        """
+        while self.token[0] in [WORD, STRING]:
+            self.parseKeyValuePair(parent)
+
+    def parseKeyValuePair(self, parent):
+        """
+        Parse a key-value pair, and add it to the provided L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        comment = self.comment
+        tt, tv = self.token
+        if tt == WORD:
+            key = tv
+            suffix = tv
+        elif tt == STRING:
+            key = eval(tv)
+            suffix = '[%s]' % tv
+        else:
+            msg = "%s: expecting word or string, found %r"
+            raise ConfigFormatError(msg % (self.location(), tv))
+        self.token = self.getToken()
+        # for now, we allow key on its own as a short form of key : True
+        if self.token[0] == COLON:
+            self.token = self.getToken()
+            value = self.parseValue(parent, suffix)
+        else:
+            value = True
+        try:
+            parent.addMapping(key, value, comment)
+        except Exception as e:
+            raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
+                                    self.token[1]))
+        tt = self.token[0]
+        if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
+            msg = "%s: expecting one of EOF, WORD, STRING, \
+RCURLY, COMMA, found %r"
+            raise ConfigFormatError(msg  % (self.location(), self.token[1]))
+        if tt == COMMA:
+            self.token = self.getToken()
+
+    def parseValue(self, parent, suffix):
+        """
+        Parse a value.
+
+        @param parent: The container to which the value will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: The value
+        @rtype: any
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
+                  TRUE, FALSE, NONE, BACKTICK, MINUS]:
+            rv = self.parseScalar()
+        elif tt == LBRACK:
+            rv = self.parseSequence(parent, suffix)
+        elif tt in [LCURLY, AT]:
+            rv = self.parseMapping(parent, suffix)
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseSequence(self, parent, suffix):
+        """
+        Parse a sequence.
+
+        @param parent: The container to which the sequence will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Sequence} instance representing the sequence.
+        @rtype: L{Sequence}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        rv = Sequence(parent)
+        rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.match(LBRACK)
+        comment = self.comment
+        tt = self.token[0]
+        while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
+                     TRUE, FALSE, NONE, BACKTICK]:
+            suffix = '[%d]' % len(rv)
+            value = self.parseValue(parent, suffix)
+            rv.append(value, comment)
+            tt = self.token[0]
+            comment = self.comment
+            if tt == COMMA:
+                self.match(COMMA)
+                tt = self.token[0]
+                comment = self.comment
+                continue
+        self.match(RBRACK)
+        return rv
+
+    def parseMapping(self, parent, suffix):
+        """
+        Parse a mapping.
+
+        @param parent: The container to which the mapping will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Mapping} instance representing the mapping.
+        @rtype: L{Mapping}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        if self.token[0] == LCURLY:
+            self.match(LCURLY)
+            rv = Mapping(parent)
+            rv.setPath(
+               makePath(object.__getattribute__(parent, 'path'), suffix))
+            self.parseMappingBody(rv)
+            self.match(RCURLY)
+        else:
+            self.match(AT)
+            _, fn = self.match(STRING)
+            rv = Config(eval(fn), parent)
+        return rv
+
+    def parseScalar(self):
+        """
+        Parse a scalar - a terminal value such as a string or number, or
+        an L{Expression} or L{Reference}.
+
+        @return: the parsed scalar
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseTerm()
+        tt = self.token[0]
+        while tt in [PLUS, MINUS]:
+            self.match(tt)
+            rhs = self.parseTerm()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseTerm(self):
+        """
+        Parse a term in an additive expression (a + b, a - b)
+
+        @return: the parsed term
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseFactor()
+        tt = self.token[0]
+        while tt in [STAR, SLASH, MOD]:
+            self.match(tt)
+            rhs = self.parseFactor()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseFactor(self):
+        """
+        Parse a factor in an multiplicative expression (a * b, a / b, a % b)
+
+        @return: the parsed factor
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
+            rv = self.token[1]
+            if tt != WORD:
+                rv = eval(rv)
+            self.match(tt)
+        elif tt == LPAREN:
+            self.match(LPAREN)
+            rv = self.parseScalar()
+            self.match(RPAREN)
+        elif tt == DOLLAR:
+            self.match(DOLLAR)
+            rv = self.parseReference(DOLLAR)
+        elif tt == BACKTICK:
+            self.match(BACKTICK)
+            rv = self.parseReference(BACKTICK)
+            self.match(BACKTICK)
+        elif tt == MINUS:
+            self.match(MINUS)
+            rv = -self.parseScalar()
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseReference(self, type):
+        """
+        Parse a reference.
+
+        @return: the parsed reference
+        @rtype: L{Reference}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        word = self.match(WORD)
+        rv = Reference(self.config, type, word[1])
+        while self.token[0] in [DOT, LBRACK2]:
+            self.parseSuffix(rv)
+        return rv
+
+    def parseSuffix(self, ref):
+        """
+        Parse a reference suffix.
+
+        @param ref: The reference of which this suffix is a part.
+        @type ref: L{Reference}.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt == DOT:
+            self.match(DOT)
+            word = self.match(WORD)
+            ref.addElement(DOT, word[1])
+        else:
+            self.match(LBRACK2)
+            tt, tv = self.token
+            if tt not in [NUMBER, STRING]:
+                raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
+            self.token = self.getToken()
+            tv = eval(tv)
+            self.match(RBRACK)
+            ref.addElement(LBRACK, tv)
+
+def defaultMergeResolve(map1, map2, key):
+    """
+    A default resolver for merge conflicts. Returns a string
+    indicating what action to take to resolve the conflict.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    @return: One of "merge", "append", "mismatch" or "overwrite"
+             indicating what action should be taken. This should
+             be appropriate to the objects being merged - e.g.
+             there is no point returning "merge" if the two objects
+             are instances of L{Sequence}.
+    @rtype: str
+    """
+    obj1 = map1[key]
+    obj2 = map2[key]
+    if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
+        rv = "merge"
+    elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
+        rv = "append"
+    else:
+        rv = "mismatch"
+    return rv
+
+def overwriteMergeResolve(map1, map2, key):
+    """
+    An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
+    but where a "mismatch" is detected, returns "overwrite" instead.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    """
+    rv = defaultMergeResolve(map1, map2, key)
+    if rv == "mismatch":
+        rv = "overwrite"
+    return rv
+
+class ConfigMerger(object):
+    """
+    This class is used for merging two configurations. If a key exists in the
+    merge operand but not the merge target, then the entry is copied from the
+    merge operand to the merge target. If a key exists in both configurations,
+    then a resolver (a callable) is called to decide how to handle the
+    conflict.
+    """
+
+    def __init__(self, resolver=defaultMergeResolve):
+        """
+        Initialise an instance.
+
+        @param resolver:
+        @type resolver: A callable which takes the argument list
+        (map1, map2, key) where map1 is the mapping being merged into,
+        map2 is the merge operand and key is the clashing key. The callable
+        should return a string indicating how the conflict should be resolved.
+        For possible return values, see L{defaultMergeResolve}. The default
+        value preserves the old behaviour
+        """
+        self.resolver = resolver
+
+    def merge(self, merged, mergee):
+        """
+        Merge two configurations. The second configuration is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param merged: The configuration to merge into.
+        @type merged: L{Config}.
+        @param mergee: The configuration to merge.
+        @type mergee: L{Config}.
+        """
+        self.mergeMapping(merged, mergee)
+
+    def overwriteKeys(self, map1, seq2):
+        """
+        Renint variables. The second mapping is unchanged,
+        and the first is changed depending the keys of the second mapping.
+        @param map1: The mapping to reinit keys into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping container reinit information.
+        @type map2: L{Mapping}.
+        """
+
+        overwrite_list = object.__getattribute__(seq2, 'data')
+        for overwrite_instruction in overwrite_list:
+            object.__setattr__(overwrite_instruction, 'parent', map1)
+            if "__condition__" in overwrite_instruction.keys():
+                overwrite_condition = overwrite_instruction["__condition__"]
+                if eval(overwrite_condition, globals(), map1):
+                    for key in overwrite_instruction.keys():
+                        if key == "__condition__":
+                            continue
+                        try:
+                            exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
+                        except:
+                            exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+            else:
+                for key in overwrite_instruction.keys():
+                    try:
+                        exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
+                    except:
+                        exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+
+    def mergeMapping(self, map1, map2):
+        """
+        Merge two mappings recursively. The second mapping is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param map1: The mapping to merge into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping to merge.
+        @type map2: L{Mapping}.
+        """
+        keys = map1.keys()
+        global __resolveOverwrite__
+        for key in map2.keys():
+            if __resolveOverwrite__ and key == "__overwrite__":
+                self.overwriteKeys(map1,map2[key])
+
+            elif key not in keys:
+                map1[key] = map2[key]
+                if isinstance(map1[key], Container) :
+                    object.__setattr__(map1[key], 'parent', map1)
+            else:
+                obj1 = map1[key]
+                obj2 = map2[key]
+                decision = self.resolver(map1, map2, key)
+                if decision == "merge":
+                    self.mergeMapping(obj1, obj2)
+                elif decision == "append":
+                    self.mergeSequence(obj1, obj2)
+                elif decision == "overwrite":
+                    map1[key] = obj2
+                    if isinstance(map1[key], Container):
+                        object.__setattr__(map1[key], 'parent', map1)
+                elif decision == "mismatch":
+                    self.handleMismatch(obj1, obj2)
+                else:
+                    msg = "unable to merge: don't know how to implement %r"
+                    raise ValueError(msg % decision)
+
+    def mergeSequence(self, seq1, seq2):
+        """
+        Merge two sequences. The second sequence is unchanged,
+        and the first is changed to have the elements of the second
+        appended to it.
+
+        @param seq1: The sequence to merge into.
+        @type seq1: L{Sequence}.
+        @param seq2: The sequence to merge.
+        @type seq2: L{Sequence}.
+        """
+        data1 = object.__getattribute__(seq1, 'data')
+        data2 = object.__getattribute__(seq2, 'data')
+        for obj in data2:
+            data1.append(obj)
+        comment1 = object.__getattribute__(seq1, 'comments')
+        comment2 = object.__getattribute__(seq2, 'comments')
+        for obj in comment2:
+            comment1.append(obj)
+
+    def handleMismatch(self, obj1, obj2):
+        """
+        Handle a mismatch between two objects.
+
+        @param obj1: The object to merge into.
+        @type obj1: any
+        @param obj2: The object to merge.
+        @type obj2: any
+        """
+        raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
+
+class ConfigList(list):
+    """
+    This class implements an ordered list of configurations and allows you
+    to try getting the configuration from each entry in turn, returning
+    the first successfully obtained value.
+    """
+
+    def getByPath(self, path):
+        """
+        Obtain a value from the first configuration in the list which defines
+        it.
+
+        @param path: The path of the value to retrieve.
+        @type path: str
+        @return: The value from the earliest configuration in the list which
+        defines it.
+        @rtype: any
+        @raise ConfigError: If no configuration in the list has an entry with
+        the specified path.
+        """
+        found = False
+        rv = None
+        for entry in self:
+            try:
+                rv = entry.getByPath(path)
+                found = True
+                break
+            except ConfigError:
+                pass
+        if not found:
+            raise ConfigError("unable to resolve %r" % path)
+        return rv
diff --git a/src/common/options.py b/src/common/options.py
new file mode 100644 (file)
index 0000000..4a0f59d
--- /dev/null
@@ -0,0 +1,168 @@
+#!/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 command
+    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 + " 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'])
+        
+        return optResult, args
+
diff --git a/src/common/printcolors.py b/src/common/printcolors.py
new file mode 100755 (executable)
index 0000000..bad832a
--- /dev/null
@@ -0,0 +1,112 @@
+#!/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))
+
+# print a text with colors
+def printc(txt, code=''):
+    # no code means 'auto mode'
+    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):
+    return printc(txt, COLOR_INFO)
+
+def printcError(txt):
+    return printc(txt, COLOR_ERROR)
+
+def printcWarning(txt):
+    return printc(txt, COLOR_WARNING)
+
+def printcHeader(txt):
+    return printc(txt, COLOR_HEADER)
+
+def printcLabel(txt):
+    return printc(txt, COLOR_LABEL)
+
+def printcSuccess(txt):
+    return printc(txt, COLOR_SUCCESS)
+
+def printcHighlight(txt):
+    return printc(txt, COLOR_HIGLIGHT)
+
+def cleancolor(message):
+    message = message.replace('\033[0m', '')
+    for i in __code_range__:
+        message = message.replace('\033[%dm' % i, '')
+    return message
+
+# shortcut method to print a label and a value with the info color
+def print_value(logger, label, value, level=1, suffix=""):
+    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):
+    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():
+    print("colormap:")
+    print("{")
+    for k in sorted(__colormap__.keys()):
+        codes = __colormap__[k].split('\033[')
+        codes = filter(lambda l: len(l) > 0, codes)
+        codes = map(lambda l: l[:-1], codes)
+        print(printc("  %s: '%s', " % (k, ';'.join(codes)), k))
+    print("}")
+
+
diff --git a/src/config.py b/src/config.py
new file mode 100644 (file)
index 0000000..2ea6d18
--- /dev/null
@@ -0,0 +1,214 @@
+#!/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 common
+import platform
+import datetime
+
+# 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.")
+
+'''
+class MergeHandler:
+    def __init__(self):
+        pass
+
+    def __call__(self, map1, map2, key):
+        if '__overwrite__' in map2 and key in map2.__overwrite__:
+            return "overwrite"
+        else:
+            return common.config_pyconf.overwriteMergeResolve(map1, map2, key)
+'''
+
+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.config_pyconf.Config(os.path.join(var['dataDir'], "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
+        
+        # Forcing architecture with env variable ARCH on Windows
+        if common.architecture.is_windows() and "ARCH" in os.environ :
+            bitsdict={"Win32":"32","Win64":"64"}
+            nb_bits = bitsdict[os.environ["ARCH"]]
+        else :
+            nb_bits = common.architecture.get_nb_bit()
+
+        var['dist_name'] = dist_name
+        var['dist_version'] = dist_version
+        var['dist'] = dist
+        var['arch'] = dist + '_' + nb_bits
+        var['bits'] = nb_bits
+        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
+        # particular win case 
+        if common.architecture.is_windows() :
+            var['hostname'] = node_name+'-'+nb_bits
+
+        # 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 : TO DO
+        :param sections str: The command that is called.
+        :return: The list of all the overwrites of the command line.
+        :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:
+            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 TODO
+        :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.config_pyconf.Config'
+        '''        
+        
+        # create a ConfigMerger to handle merge
+        merger = common.config_pyconf.ConfigMerger()#MergeHandler())
+        
+        # create the configuration instance
+        cfg = common.config_pyconf.Config()
+        
+        # =======================================================================================
+        # create VARS section
+        var = self._create_vars(application=application, command=command, dataDir=dataDir)
+        # add VARS to config
+        cfg.VARS = common.config_pyconf.Mapping(cfg)
+        for variable in var:
+            cfg.VARS[variable] = var[variable]
+
+        for rule in self.get_command_line_overrides(options, ["VARS"]):
+            exec('cfg.' + rule) # this cannot be factorized because of the exec
+        
+        return cfg
+    
+    
+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.config_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 :
+    :return: The final config.
+    :rtype: class 'common.config_pyconf.Config'
+    '''            
+    
+    # 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.config_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 run(args, runner):
+    (options, args) = parser.parse_args(args)
+    print('Je suis dans la commande config ! Bien jouĂ© ! COUCOU')
+    if options.value:
+        print_value(runner.cfg, options.value, True, level=0, show_full_path=False)
+    
+    runner.config_copy('-v VARS')
\ No newline at end of file
diff --git a/src/salomeTools.py b/src/salomeTools.py
new file mode 100755 (executable)
index 0000000..bbfa571
--- /dev/null
@@ -0,0 +1,156 @@
+#!/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
+'''
+
+import os
+import sys
+import imp
+import types
+import common.options
+import config
+
+def copy_func(f, name=None):
+    '''
+    return a function with same code, globals, defaults, closure, and 
+    name (or provide a new name)
+    '''
+
+    fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
+        f.__defaults__, f.__closure__)
+    # in case f was given attrs (note this dict is a shallow copy):
+    fn.__dict__.update(f.__dict__) 
+    return fn
+
+# get path to salomeTools sources
+srcdir = os.path.dirname(os.path.realpath(__file__))
+
+def find_command_list():
+    cmd_list = []
+    for item in os.listdir(srcdir):
+        if item.endswith('.py') and item!='salomeTools.py':
+            cmd_list.append(item[:-len('.py')])
+    return cmd_list
+
+# The list of valid salomeTools commands
+#lCommand = ['config', 'compile', 'prepare']
+lCommand = find_command_list()
+
+# 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.")
+
+class salomeTools(object):
+    def __init__(self, options, dataDir=None):
+        '''Initialization
+        '''
+        self.__dict__ = dict()
+        self.options = options
+        self.dataDir = dataDir
+        # set the commands
+        self.__setCommands__(srcdir)
+
+    def __getattr__(self, name):
+        if name in self.__dict__:
+            return self.__dict__[name]
+        else:
+            raise AttributeError(name + " is not a valid command")
+
+    def __setattr__(self, name, value):
+        object.__setattr__(self,name,value)
+
+    def __setCommands__(self, dirPath):
+        for nameCmd in lCommand:
+            (file_, pathname, description) = imp.find_module(nameCmd, [dirPath])
+            module = imp.load_module(nameCmd, file_, pathname, description)
+                       
+            def run_command(args):
+                print('Je suis dans l\'initialisation de la commande ' + __name__)
+                argv = args.split(" ")
+                
+                # first argument is the APPLICATION
+                appliToLoad = None
+                if len(argv) > 0 and argv[0][0] != "-":
+                    appliToLoad = argv[0].rstrip('*')
+                    argv = argv[1:]
+                
+                # read the configuration from all the pyconf files    
+                cfgManager = config.ConfigManager()
+                self.cfg = cfgManager.getConfig(dataDir=self.dataDir, application=appliToLoad)
+                
+                return __module__.run(argv, self)
+
+            globals_up = {}
+            globals_up.update(run_command.__globals__)
+            globals_up.update({'__name__': nameCmd, '__module__' : module})
+            fn = types.FunctionType(run_command.__code__, globals_up, run_command.__name__,run_command.__defaults__, run_command.__closure__)
+
+            self.__setattr__(nameCmd, fn)
+             
+
+
+
+def print_help(options):
+    #from config import ConfigManager
+    #cfgManager = ConfigManager(STAND_ALONE_VERSION)
+    #cfg = cfgManager.getConfig(None, options)
+    #print_version(cfg)
+    #print
+
+    print(common.printcolors.printcHeader( "Usage: " ) + "sat [sat_options] <command> [product] [command_options]\n")
+
+    parser.print_help()
+
+    # parse the src directory to list the available commands.
+    print(common.printcolors.printcHeader("Available commands are:\n"))
+    for command in lCommand:
+        print(" - %s" % (command))
+
+    print(common.printcolors.printcHeader("\nGetting the help for a specific command: " + "sat --help <command>\n"))
+
+def write_exception(exc):
+    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
+    
+    # Read the salomeTools options (the list of possible options is at the beginning of this file)
+    try:
+        (options, args) = parser.parse_args(sys.argv[1:])
+    except Exception as exc:
+        write_exception(exc)
+        sys.exit(-1)
+    
+    if len(args) == 0:
+        # no options => show help
+        print_help(options)
+    
+    sat = salomeTools('')
+    sat.config('-v VARS.python')
+    
+
+