]> SALOME platform Git repositories - tools/sat.git/commitdiff
Salome HOME
Remarks from sprint 1 meeting : rename some files and directories, and minor code...
authorSerge Rehbinder <serge.rehbinder@cea.fr>
Tue, 2 Feb 2016 14:31:18 +0000 (15:31 +0100)
committerSerge Rehbinder <serge.rehbinder@cea.fr>
Tue, 2 Feb 2016 14:31:18 +0000 (15:31 +0100)
34 files changed:
data/software_pyconf/softA.pyconf [deleted file]
data/software_pyconf/softB.pyconf [deleted file]
data/softwares/softA.pyconf [new file with mode: 0644]
data/softwares/softB.pyconf [new file with mode: 0644]
doc/Makefile
doc/source/commands/config.rst [deleted file]
doc/source/conf.py [deleted file]
doc/source/index.rst [deleted file]
doc/src/commands/apidoc/modules.rst [new file with mode: 0644]
doc/src/commands/apidoc/src.common.rst [new file with mode: 0644]
doc/src/commands/apidoc/src.rst [new file with mode: 0644]
doc/src/commands/config.rst [new file with mode: 0644]
doc/src/conf.py [new file with mode: 0644]
doc/src/index.rst [new file with mode: 0644]
src/common/__init__.py
src/common/config_pyconf.py [deleted file]
src/common/fileSystem.py [deleted file]
src/common/pyconf.py [new file with mode: 0644]
src/common/system.py [new file with mode: 0644]
src/config.py
src/salomeTools.py
test/__TOOLS__/HTMLTestRunner.py [deleted file]
test/__TOOLS__/tools.py [deleted file]
test/_testTools/HTMLTestRunner.py [new file with mode: 0644]
test/_testTools/tools.py [new file with mode: 0644]
test/config/create_user_pyconf.py [new file with mode: 0644]
test/config/integration/create_user_pyconf.py [deleted file]
test/config/integration/option_copy.py [deleted file]
test/config/integration/option_value.py [deleted file]
test/config/integration/option_value_2.py [deleted file]
test/config/option_copy.py [new file with mode: 0644]
test/config/option_value.py [new file with mode: 0644]
test/config/option_value_2.py [new file with mode: 0644]
test/run_all.sh

diff --git a/data/software_pyconf/softA.pyconf b/data/software_pyconf/softA.pyconf
deleted file mode 100644 (file)
index 0a1f036..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-SOFTWARE :
-{
-       softA :
-       {
-               name : "softA"
-           has_gui : "no"
-           compile_method : "cmake" # ou autotools, ou script
-           get_method : "git" # "archive", embedded", "native" "fixed"
-           cvs_info:
-           {
-               server : $SITE.prepare.default_cvs_server
-               module_base : $SITE.prepare.cvs_dir + $name
-               source : 'softA_SRC'
-               tag : ''
-           }
-           git_info:
-           {
-               repo : $SITE.prepare.default_git_server + $VARS.sep + $name
-               repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name
-               tag : $APPLICATION.default_version_to_download
-           }
-           archive_info:
-           {
-               archive_name : $SITE.prepare.archive_dir + $VARS.sep +  $name + '.tar.gz'
-           }
-           environ :
-           {
-               "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
-               "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
-                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}"
-                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"]
-           }
-           depend : []
-           opt_depend : []
-       }
-}
\ No newline at end of file
diff --git a/data/software_pyconf/softB.pyconf b/data/software_pyconf/softB.pyconf
deleted file mode 100644 (file)
index 3a34586..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-SOFTWARE :
-{
-       softB :
-       {
-               name : "softB"
-           has_gui : "no"
-           compile_method : "cmake" # ou autotools, ou script
-           get_method : "git" # "archive", embedded", "native" "fixed"
-           cvs_info:
-           {
-               server : $SITE.prepare.default_cvs_server
-               module_base : $SITE.prepare.cvs_dir + $name
-               source : 'softB_SRC'
-               tag : ''
-           }
-           git_info:
-           {
-               repo : $SITE.prepare.default_git_server + $VARS.sep + $name
-               repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name
-               tag : $APPLICATION.default_version_to_download
-           }
-           archive_info:
-           {
-               archive_name : $SITE.prepare.archive_dir + $VARS.sep +  $name + '.tar.gz'
-           }
-           environ :
-           {
-               "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
-               "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
-                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}"
-                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"]
-           }
-           depend : ['softA']
-           opt_depend : []
-       }
-}
\ No newline at end of file
diff --git a/data/softwares/softA.pyconf b/data/softwares/softA.pyconf
new file mode 100644 (file)
index 0000000..0a1f036
--- /dev/null
@@ -0,0 +1,36 @@
+SOFTWARE :
+{
+       softA :
+       {
+               name : "softA"
+           has_gui : "no"
+           compile_method : "cmake" # ou autotools, ou script
+           get_method : "git" # "archive", embedded", "native" "fixed"
+           cvs_info:
+           {
+               server : $SITE.prepare.default_cvs_server
+               module_base : $SITE.prepare.cvs_dir + $name
+               source : 'softA_SRC'
+               tag : ''
+           }
+           git_info:
+           {
+               repo : $SITE.prepare.default_git_server + $VARS.sep + $name
+               repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name
+               tag : $APPLICATION.default_version_to_download
+           }
+           archive_info:
+           {
+               archive_name : $SITE.prepare.archive_dir + $VARS.sep +  $name + '.tar.gz'
+           }
+           environ :
+           {
+               "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
+               "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
+                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}"
+                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"]
+           }
+           depend : []
+           opt_depend : []
+       }
+}
\ No newline at end of file
diff --git a/data/softwares/softB.pyconf b/data/softwares/softB.pyconf
new file mode 100644 (file)
index 0000000..3a34586
--- /dev/null
@@ -0,0 +1,36 @@
+SOFTWARE :
+{
+       softB :
+       {
+               name : "softB"
+           has_gui : "no"
+           compile_method : "cmake" # ou autotools, ou script
+           get_method : "git" # "archive", embedded", "native" "fixed"
+           cvs_info:
+           {
+               server : $SITE.prepare.default_cvs_server
+               module_base : $SITE.prepare.cvs_dir + $name
+               source : 'softB_SRC'
+               tag : ''
+           }
+           git_info:
+           {
+               repo : $SITE.prepare.default_git_server + $VARS.sep + $name
+               repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name
+               tag : $APPLICATION.default_version_to_download
+           }
+           archive_info:
+           {
+               archive_name : $SITE.prepare.archive_dir + $VARS.sep +  $name + '.tar.gz'
+           }
+           environ :
+           {
+               "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
+               "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib"
+                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}"
+                   "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"]
+           }
+           depend : ['softA']
+           opt_depend : []
+       }
+}
\ No newline at end of file
index f79ee7764d2e0ff614126e3892702f40d0ace5b8..a514380e7d2d1ed94ac5047960cda81378b7b184 100644 (file)
@@ -10,7 +10,7 @@ BUILDDIR      = build
 # Internal variables.
 PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) src
 SILENT          = &>/dev/null
 
 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
@@ -139,4 +139,4 @@ doctest:
              "results in $(BUILDDIR)/doctest/output.txt."
 
 apidoc:
-       @sphinx-apidoc -o source/commands/apidoc ../src
+       @sphinx-apidoc -o src/commands/apidoc ../src
diff --git a/doc/source/commands/config.rst b/doc/source/commands/config.rst
deleted file mode 100644 (file)
index c1f26e5..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-******
-config
-******
-
-Description
-===========
-The **config** manages sat configuration.
-
-Usage
-=====
-* List available products: ::
-  
-    sat config --list
-
-* Copy a product into the user personal directory: ::
-  
-    sat config <product> --copy [new_name]
-
-* Edit the user configuration file: ::
-
-    sat config --edit
-
-* Edit a product: ::
-
-    sat config <product> --edit
-
-* Get the value of a parameter: ::
-
-    sat config --value <parameter_path>
-    sat config --value TOOLS.prepare.cvs_server
-
-    sat config <product> --value <parameter_path>
-    sat config SALOME_7_7_1 --value PRODUCT.out_dir
-
-
-Configuration
-=============
-* SITE.config
-
-  * **configPath**: list of directories where to find products files.
-
-* USER
-
-  * **editor**: command to use to start an editor (by default vi),
-  * **browser**: command to use to start a browser (by default firefox),
-  * **pdf_viewer**: command to use to start a pdf viewer (by default evince).
diff --git a/doc/source/conf.py b/doc/source/conf.py
deleted file mode 100644 (file)
index 39d1117..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# salomeTools documentation build configuration file, created by
-# sphinx-quickstart on Wed Sep 14 11:55:14 2011.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'salomeTools'
-copyright = u'2010-2016, CEA'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '5.0'
-# The full version, including alpha/beta/rc tags.
-release = '5.0.0dev'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-# default sphinxdoc scrolls agogo traditional nature haiku
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = "images/salomeTools.ico"
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-html_use_smartypants = False
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'salomeToolsdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-latex_paper_size = 'a4'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'salomeTools.tex', u'salomeTools Documentation',
-   u'CEA', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'salometools', u'salomeTools Documentation',
-     [u'CEA'], 1)
-]
-
-# Append source folder to path in order to enable autodoc
-sys.path.append(os.path.join('..'))
\ No newline at end of file
diff --git a/doc/source/index.rst b/doc/source/index.rst
deleted file mode 100644 (file)
index 30b3450..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-************
-SAlome Tools
-************
-
-.. note::
-    
-    This documentation is under construction.
-
-SAlomeTools (sat) is a suite of commands that can be used to perform operations on SALOME.
-
-For example, sat allows you to compile SALOME's modules or prerequisites, create an application, run tests, create a package...
-
-Quick start
-===========
-
-.. toctree::
-    :maxdepth: 1
-
-    Installation of salomeTools (to be competed)
-    Configuration (to be competed)
-    Usage of salomeTools (to be competed)
-
-List of Commands
-================
-
-.. toctree::
-   :maxdepth: 1
-   
-   config <commands/config>
-
-Code documentation
-==================
-.. toctree::
-   :maxdepth: 1
-   
-   SAT modules <commands/apidoc/modules.rst>
-
-
-
-Release Notes
-=============
-Here are the release notes for sat:
-
-.. toctree::
-    :maxdepth: 1
-
-    Release Notes 5.0.0 (to be completed)
-
diff --git a/doc/src/commands/apidoc/modules.rst b/doc/src/commands/apidoc/modules.rst
new file mode 100644 (file)
index 0000000..e9ff8ac
--- /dev/null
@@ -0,0 +1,7 @@
+src
+===
+
+.. toctree::
+   :maxdepth: 4
+
+   src
diff --git a/doc/src/commands/apidoc/src.common.rst b/doc/src/commands/apidoc/src.common.rst
new file mode 100644 (file)
index 0000000..90d59ee
--- /dev/null
@@ -0,0 +1,54 @@
+src.common package
+==================
+
+Submodules
+----------
+
+src.common.architecture module
+------------------------------
+
+.. automodule:: src.common.architecture
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.common.config_pyconf module
+-------------------------------
+
+.. automodule:: src.common.config_pyconf
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.common.fileSystem module
+----------------------------
+
+.. automodule:: src.common.fileSystem
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.common.options module
+-------------------------
+
+.. automodule:: src.common.options
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.common.printcolors module
+-----------------------------
+
+.. automodule:: src.common.printcolors
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: src.common
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/src/commands/apidoc/src.rst b/doc/src/commands/apidoc/src.rst
new file mode 100644 (file)
index 0000000..0c66dd5
--- /dev/null
@@ -0,0 +1,45 @@
+src package
+===========
+
+Subpackages
+-----------
+
+.. toctree::
+
+    src.common
+
+Submodules
+----------
+
+src.config module
+-----------------
+
+.. automodule:: src.config
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.salomeTools module
+----------------------
+
+.. automodule:: src.salomeTools
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+src.test_command module
+-----------------------
+
+.. automodule:: src.test_command
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+
+Module contents
+---------------
+
+.. automodule:: src
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/src/commands/config.rst b/doc/src/commands/config.rst
new file mode 100644 (file)
index 0000000..c1f26e5
--- /dev/null
@@ -0,0 +1,46 @@
+******
+config
+******
+
+Description
+===========
+The **config** manages sat configuration.
+
+Usage
+=====
+* List available products: ::
+  
+    sat config --list
+
+* Copy a product into the user personal directory: ::
+  
+    sat config <product> --copy [new_name]
+
+* Edit the user configuration file: ::
+
+    sat config --edit
+
+* Edit a product: ::
+
+    sat config <product> --edit
+
+* Get the value of a parameter: ::
+
+    sat config --value <parameter_path>
+    sat config --value TOOLS.prepare.cvs_server
+
+    sat config <product> --value <parameter_path>
+    sat config SALOME_7_7_1 --value PRODUCT.out_dir
+
+
+Configuration
+=============
+* SITE.config
+
+  * **configPath**: list of directories where to find products files.
+
+* USER
+
+  * **editor**: command to use to start an editor (by default vi),
+  * **browser**: command to use to start a browser (by default firefox),
+  * **pdf_viewer**: command to use to start a pdf viewer (by default evince).
diff --git a/doc/src/conf.py b/doc/src/conf.py
new file mode 100644 (file)
index 0000000..39d1117
--- /dev/null
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+#
+# salomeTools documentation build configuration file, created by
+# sphinx-quickstart on Wed Sep 14 11:55:14 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'salomeTools'
+copyright = u'2010-2016, CEA'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '5.0'
+# The full version, including alpha/beta/rc tags.
+release = '5.0.0dev'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+# default sphinxdoc scrolls agogo traditional nature haiku
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = "images/salomeTools.ico"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = False
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'salomeToolsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+latex_paper_size = 'a4'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'salomeTools.tex', u'salomeTools Documentation',
+   u'CEA', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'salometools', u'salomeTools Documentation',
+     [u'CEA'], 1)
+]
+
+# Append source folder to path in order to enable autodoc
+sys.path.append(os.path.join('..'))
\ No newline at end of file
diff --git a/doc/src/index.rst b/doc/src/index.rst
new file mode 100644 (file)
index 0000000..30b3450
--- /dev/null
@@ -0,0 +1,49 @@
+************
+SAlome Tools
+************
+
+.. note::
+    
+    This documentation is under construction.
+
+SAlomeTools (sat) is a suite of commands that can be used to perform operations on SALOME.
+
+For example, sat allows you to compile SALOME's modules or prerequisites, create an application, run tests, create a package...
+
+Quick start
+===========
+
+.. toctree::
+    :maxdepth: 1
+
+    Installation of salomeTools (to be competed)
+    Configuration (to be competed)
+    Usage of salomeTools (to be competed)
+
+List of Commands
+================
+
+.. toctree::
+   :maxdepth: 1
+   
+   config <commands/config>
+
+Code documentation
+==================
+.. toctree::
+   :maxdepth: 1
+   
+   SAT modules <commands/apidoc/modules.rst>
+
+
+
+Release Notes
+=============
+Here are the release notes for sat:
+
+.. toctree::
+    :maxdepth: 1
+
+    Release Notes 5.0.0 (to be completed)
+
index bad9eb52e9128f24ce88c72918d879abeec9600d..629fb4f40ed1338be3f9c3d7313bb3cdd105a869 100644 (file)
 
 import os
 
-from . import config_pyconf
+from . import pyconf
 from . import architecture
 from . import printcolors
 from . import options
-from . import fileSystem
+from . import system
 
 class SatException(Exception):
     '''rename Exception Class
@@ -40,7 +40,7 @@ def ensure_path_exists(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.config_pyconf.Config': The config.
+    :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")
diff --git a/src/common/config_pyconf.py b/src/common/config_pyconf.py
deleted file mode 100644 (file)
index 53bd184..0000000
+++ /dev/null
@@ -1,1704 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-
-
-# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation, and that the name of Vinay Sajip
-# not be used in advertising or publicity pertaining to distribution
-# of the software without specific, written prior permission.
-# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
-# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""
-This is a configuration module for Python.
-
-This module should work under Python versions >= 2.2, and cannot be used with
-earlier versions since it uses new-style classes.
-
-Development and testing has only been carried out (so far) on Python 2.3.4 and
-Python 2.4.2. See the test module (test_config.py) included in the
-U{distribution<http://www.red-dove.com/python_config.html|_blank>} (follow the
-download link).
-
-A simple example - with the example configuration file::
-
-    messages:
-    [
-      {
-        stream : `sys.stderr`
-        message: 'Welcome'
-        name: 'Harry'
-      }
-      {
-        stream : `sys.stdout`
-        message: 'Welkom'
-        name: 'Ruud'
-      }
-      {
-        stream : $messages[0].stream
-        message: 'Bienvenue'
-        name: Yves
-      }
-    ]
-
-a program to read the configuration would be::
-
-    from config import Config
-
-    f = file('simple.cfg')
-    cfg = Config(f)
-    for m in cfg.messages:
-        s = '%s, %s' % (m.message, m.name)
-        try:
-            print >> m.stream, s
-        except IOError, e:
-            print e
-
-which, when run, would yield the console output::
-
-    Welcome, Harry
-    Welkom, Ruud
-    Bienvenue, Yves
-
-See U{this tutorial<http://www.red-dove.com/python_config.html|_blank>} for more
-information.
-
-#modified for salomeTools
-@version: 0.3.7.1
-
-@author: Vinay Sajip
-
-@copyright: Copyright (C) 2004-2007 Vinay Sajip. All Rights Reserved.
-
-
-@var streamOpener: The default stream opener. This is a factory function which
-takes a string (e.g. filename) and returns a stream suitable for reading. If
-unable to open the stream, an IOError exception should be thrown.
-
-The default value of this variable is L{defaultStreamOpener}. For an example
-of how it's used, see test_config.py (search for streamOpener).
-"""
-
-__author__  = "Vinay Sajip <vinay_sajip@red-dove.com>"
-__status__  = "alpha"
-__version__ = "0.3.7.1" #modified for salomeTools
-__date__    = "05 October 2007"
-
-import codecs
-import os
-import sys
-
-WORD = 'a'
-NUMBER = '9'
-STRING = '"'
-EOF = ''
-LCURLY = '{'
-RCURLY = '}'
-LBRACK = '['
-LBRACK2 = 'a['
-RBRACK = ']'
-LPAREN = '('
-LPAREN2 = '(('
-RPAREN = ')'
-DOT = '.'
-COMMA = ','
-COLON = ':'
-AT = '@'
-PLUS = '+'
-MINUS = '-'
-STAR = '*'
-SLASH = '/'
-MOD = '%'
-BACKTICK = '`'
-DOLLAR = '$'
-TRUE = 'True'
-FALSE = 'False'
-NONE = 'None'
-
-WORDCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
-
-if sys.platform == 'win32':
-    NEWLINE = '\r\n'
-elif os.name == 'mac':
-    NEWLINE = '\r'
-else:
-    NEWLINE = '\n'
-
-try:
-    has_utf32 = True
-except:
-    has_utf32 = False
-
-class ConfigInputStream(object):
-    """
-    An input stream which can read either ANSI files with default encoding
-    or Unicode files with BOMs.
-
-    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
-    built-in support.
-    """
-    def __init__(self, stream):
-        """
-        Initialize an instance.
-
-        @param stream: The underlying stream to be read. Should be seekable.
-        @type stream: A stream (file-like object).
-        """
-        encoding = None
-        signature = stream.read(4)
-        used = -1
-        if has_utf32:
-            if signature == codecs.BOM_UTF32_LE:
-                encoding = 'utf-32le'
-            elif signature == codecs.BOM_UTF32_BE:
-                encoding = 'utf-32be'
-        if encoding is None:
-            if signature[:3] == codecs.BOM_UTF8:
-                used = 3
-                encoding = 'utf-8'
-            elif signature[:2] == codecs.BOM_UTF16_LE:
-                used = 2
-                encoding = 'utf-16le'
-            elif signature[:2] == codecs.BOM_UTF16_BE:
-                used = 2
-                encoding = 'utf-16be'
-            else:
-                used = 0
-        if used >= 0:
-            stream.seek(used)
-        if encoding:
-            reader = codecs.getreader(encoding)
-            stream = reader(stream)
-        self.stream = stream
-        self.encoding = encoding
-
-    def read(self, size):
-        if (size == 0) or (self.encoding is None):
-            rv = self.stream.read(size)
-        else:
-            rv = u''
-            while size > 0:
-                rv += self.stream.read(1)
-                size -= 1
-        return rv
-
-    def close(self):
-        self.stream.close()
-
-    def readline(self):
-        if self.encoding is None:
-            line = ''
-        else:
-            line = u''
-        while True:
-            c = self.stream.read(1)
-            if isinstance(c, bytes):
-                c = c.decode()
-            if c:
-                line += c
-            if c == '\n':
-                break
-        return line
-
-class ConfigOutputStream(object):
-    """
-    An output stream which can write either ANSI files with default encoding
-    or Unicode files with BOMs.
-
-    Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
-    built-in support.
-    """
-
-    def __init__(self, stream, encoding=None):
-        """
-        Initialize an instance.
-
-        @param stream: The underlying stream to be written.
-        @type stream: A stream (file-like object).
-        @param encoding: The desired encoding.
-        @type encoding: str
-        """
-        if encoding is not None:
-            encoding = str(encoding).lower()
-        self.encoding = encoding
-        if encoding == "utf-8":
-            stream.write(codecs.BOM_UTF8)
-        elif encoding == "utf-16be":
-            stream.write(codecs.BOM_UTF16_BE)
-        elif encoding == "utf-16le":
-            stream.write(codecs.BOM_UTF16_LE)
-        elif encoding == "utf-32be":
-            stream.write(codecs.BOM_UTF32_BE)
-        elif encoding == "utf-32le":
-            stream.write(codecs.BOM_UTF32_LE)
-
-        if encoding is not None:
-            writer = codecs.getwriter(encoding)
-            stream = writer(stream)
-        self.stream = stream
-
-    def write(self, data):
-        self.stream.write(data)
-
-    def flush(self):
-        self.stream.flush()
-
-    def close(self):
-        self.stream.close()
-
-def defaultStreamOpener(name):
-    """
-    This function returns a read-only stream, given its name. The name passed
-    in should correspond to an existing stream, otherwise an exception will be
-    raised.
-
-    This is the default value of L{streamOpener}; assign your own callable to
-    streamOpener to return streams based on names. For example, you could use
-    urllib2.urlopen().
-
-    @param name: The name of a stream, most commonly a file name.
-    @type name: str
-    @return: A stream with the specified name.
-    @rtype: A read-only stream (file-like object)
-    """
-    return ConfigInputStream(open(name, 'rb'))
-
-streamOpener = None
-
-__resolveOverwrite__ = True
-
-class ConfigError(Exception):
-    """
-    This is the base class of exceptions raised by this module.
-    """
-    pass
-
-class ConfigFormatError(ConfigError):
-    """
-    This is the base class of exceptions raised due to syntax errors in
-    configurations.
-    """
-    pass
-
-class ConfigResolutionError(ConfigError):
-    """
-    This is the base class of exceptions raised due to semantic errors in
-    configurations.
-    """
-    pass
-
-def isWord(s):
-    """
-    See if a passed-in value is an identifier. If the value passed in is not a
-    string, False is returned. An identifier consists of alphanumerics or
-    underscore characters.
-
-    Examples::
-
-        isWord('a word') ->False
-        isWord('award') -> True
-        isWord(9) -> False
-        isWord('a_b_c_') ->True
-
-    @note: isWord('9abc') will return True - not exactly correct, but adequate
-    for the way it's used here.
-
-    @param s: The name to be tested
-    @type s: any
-    @return: True if a word, else False
-    @rtype: bool
-    """
-    if type(s) != type(''):
-        return False
-    s = s.replace('_', '')
-    return s.isalnum()
-
-def makePath(prefix, suffix):
-    """
-    Make a path from a prefix and suffix.
-
-    Examples::
-
-        makePath('', 'suffix') -> 'suffix'
-        makePath('prefix', 'suffix') -> 'prefix.suffix'
-        makePath('prefix', '[1]') -> 'prefix[1]'
-
-    @param prefix:  The prefix to use. If it evaluates as false, the suffix
-                    is returned.
-    @type prefix:   str
-    @param suffix:  The suffix to use. It is either an identifier or an
-                    index in brackets.
-    @type suffix:   str
-    @return:        The path concatenation of prefix and suffix, with a
-                    dot if the suffix is not a bracketed index.
-    @rtype:         str
-
-    """
-    if not prefix:
-        rv = suffix
-    elif suffix[0] == '[':
-        rv = prefix + suffix
-    else:
-        rv = prefix + '.' + suffix
-    return rv
-
-
-class Container(object):
-    """
-    This internal class is the base class for mappings and sequences.
-
-    @ivar path: A string which describes how to get
-    to this instance from the root of the hierarchy.
-
-    Example::
-
-        a.list.of[1].or['more'].elements
-    """
-    def __init__(self, parent):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        object.__setattr__(self, 'parent', parent)
-
-    def setPath(self, path):
-        """
-        Set the path for this instance.
-        @param path: The path - a string which describes how to get
-        to this instance from the root of the hierarchy.
-        @type path: str
-        """
-        object.__setattr__(self, 'path', path)
-
-    def evaluate(self, item):
-        """
-        Evaluate items which are instances of L{Reference} or L{Expression}.
-
-        L{Reference} instances are evaluated using L{Reference.resolve},
-        and L{Expression} instances are evaluated using
-        L{Expression.evaluate}.
-
-        @param item: The item to be evaluated.
-        @type item: any
-        @return: If the item is an instance of L{Reference} or L{Expression},
-        the evaluated value is returned, otherwise the item is returned
-        unchanged.
-        """
-        if isinstance(item, Reference):
-            item = item.resolve(self)
-        elif isinstance(item, Expression):
-            item = item.evaluate(self)
-        return item
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        @raise NotImplementedError: If a subclass does not override this
-        """
-        raise NotImplementedError
-
-    def writeValue(self, value, stream, indent):
-        if isinstance(self, Mapping):
-            indstr = ' '
-        else:
-            indstr = indent * '  '
-        if isinstance(value, Reference) or isinstance(value, Expression):
-            stream.write('%s%r%s' % (indstr, value, NEWLINE))
-        else:
-            if isinstance(value, str): # and not isWord(value):
-                value = repr(value)
-            stream.write('%s%s%s' % (indstr, value, NEWLINE))
-
-class Mapping(Container):
-    """
-    This internal class implements key-value mappings in configurations.
-    """
-
-    def __init__(self, parent=None):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        Container.__init__(self, parent)
-        object.__setattr__(self, 'path', '')
-        object.__setattr__(self, 'data', {})
-        object.__setattr__(self, 'order', [])   # to preserve ordering
-        object.__setattr__(self, 'comments', {})
-
-    def __delitem__(self, key):
-        """
-        Remove an item
-        """
-        data = object.__getattribute__(self, 'data')
-        if key not in data:
-            raise AttributeError(key)
-        order = object.__getattribute__(self, 'order')
-        comments = object.__getattribute__(self, 'comments')
-        del data[key]
-        order.remove(key)
-        del comments[key]
-
-    def __getitem__(self, key):
-        data = object.__getattribute__(self, 'data')
-        if key not in data:
-            raise AttributeError(key)
-        rv = data[key]
-        return self.evaluate(rv)
-
-    __getattr__ = __getitem__
-    
-    '''
-    def __getattribute__(self, name):
-        if name == "__dict__":
-            return {}
-        if name in ["__methods__", "__members__"]:
-            return []
-        #if name == "__class__":
-        #    return ''
-        data = object.__getattribute__(self, "data")
-        useData = data.has_key(name)
-        if useData:
-            rv = getattr(data, name)
-        else:
-            rv = object.__getattribute__(self, name)
-            if rv is None:
-                raise AttributeError(name)
-        return rv
-    '''
-
-    def iteritems(self):
-        for key in self.keys():
-            yield(key, self[key])
-        raise StopIteration
-
-    def __contains__(self, item):
-        order = object.__getattribute__(self, 'order')
-        return item in order
-
-    def addMapping(self, key, value, comment, setting=False):
-        """
-        Add a key-value mapping with a comment.
-
-        @param key: The key for the mapping.
-        @type key: str
-        @param value: The value for the mapping.
-        @type value: any
-        @param comment: The comment for the key (can be None).
-        @type comment: str
-        @param setting: If True, ignore clashes. This is set
-        to true when called from L{__setattr__}.
-        @raise ConfigFormatError: If an existing key is seen
-        again and setting is False.
-        """
-        data = object.__getattribute__(self, 'data')
-        order = object.__getattribute__(self, 'order')
-        comments = object.__getattribute__(self, 'comments')
-
-        data[key] = value
-        if key not in order:
-            order.append(key)
-        elif not setting:
-            raise ConfigFormatError("repeated key: %s" % key)
-        comments[key] = comment
-
-    def __setattr__(self, name, value):
-        self.addMapping(name, value, None, True)
-
-    __setitem__ = __setattr__
-
-    def keys(self):
-        """
-        Return the keys in a similar way to a dictionary.
-        """
-        return object.__getattribute__(self, 'order')
-
-    def get(self, key, default=None):
-        """
-        Allows a dictionary-style get operation.
-        """
-        if key in self:
-            return self[key]
-        return default
-
-    def __str__(self):
-        return str(object.__getattribute__(self, 'data'))
-
-    def __repr__(self):
-        return repr(object.__getattribute__(self, 'data'))
-
-    def __len__(self):
-        return len(object.__getattribute__(self, 'order'))
-
-    def __iter__(self):
-        return self.iterkeys()
-
-    def iterkeys(self):
-        order = object.__getattribute__(self, 'order')
-        return order.__iter__()
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        """
-        indstr = indent * '  '
-        if len(self) == 0:
-            stream.write(' { }%s' % NEWLINE)
-        else:
-            if isinstance(container, Mapping):
-                stream.write(NEWLINE)
-            stream.write('%s{%s' % (indstr, NEWLINE))
-            self.__save__(stream, indent + 1)
-            stream.write('%s}%s' % (indstr, NEWLINE))
-
-    def __save__(self, stream, indent=0):
-        """
-        Save this configuration to the specified stream.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output.
-        @type indent: int
-        """
-        indstr = indent * '  '
-        order = object.__getattribute__(self, 'order')
-        data = object.__getattribute__(self, 'data')
-        maxlen = 0 # max(map(lambda x: len(x), order))
-        for key in order:
-            comment = self.comments[key]
-            if isWord(key):
-                skey = key
-            else:
-                skey = repr(key)
-            if comment:
-                stream.write('%s#%s' % (indstr, comment))
-            stream.write('%s%-*s :' % (indstr, maxlen, skey))
-            value = data[key]
-            if isinstance(value, Container):
-                value.writeToStream(stream, indent, self)
-            else:
-                self.writeValue(value, stream, indent)
-
-class Config(Mapping):
-    """
-    This class represents a configuration, and is the only one which clients
-    need to interface to, under normal circumstances.
-    """
-
-    class Namespace(object):
-        """
-        This internal class is used for implementing default namespaces.
-
-        An instance acts as a namespace.
-        """
-        def __init__(self):
-            self.sys = sys
-            self.os = os
-
-    def __init__(self, streamOrFile=None, parent=None):
-        """
-        Initializes an instance.
-
-        @param streamOrFile: If specified, causes this instance to be loaded
-        from the stream (by calling L{load}). If a string is provided, it is
-        passed to L{streamOpener} to open a stream. Otherwise, the passed
-        value is assumed to be a stream and used as is.
-        @type streamOrFile: A readable stream (file-like object) or a name.
-        @param parent: If specified, this becomes the parent of this instance
-        in the configuration hierarchy.
-        @type parent: a L{Container} instance.
-        """
-        Mapping.__init__(self, parent)
-        object.__setattr__(self, 'reader', ConfigReader(self))
-        object.__setattr__(self, 'namespaces', [Config.Namespace()])
-        if streamOrFile is not None:
-            if isinstance(streamOrFile, str) or isinstance(streamOrFile, bytes):
-                global streamOpener
-                if streamOpener is None:
-                    streamOpener = defaultStreamOpener
-                streamOrFile = streamOpener(streamOrFile)
-            load = object.__getattribute__(self, "load")
-            load(streamOrFile)
-
-    def load(self, stream):
-        """
-        Load the configuration from the specified stream. Multiple streams can
-        be used to populate the same instance, as long as there are no
-        clashing keys. The stream is closed.
-        @param stream: A stream from which the configuration is read.
-        @type stream: A read-only stream (file-like object).
-        @raise ConfigError: if keys in the loaded configuration clash with
-        existing keys.
-        @raise ConfigFormatError: if there is a syntax error in the stream.
-        """
-        reader = object.__getattribute__(self, 'reader')
-        reader.load(stream)
-        stream.close()
-
-    def addNamespace(self, ns, name=None):
-        """
-        Add a namespace to this configuration which can be used to evaluate
-        (resolve) dotted-identifier expressions.
-        @param ns: The namespace to be added.
-        @type ns: A module or other namespace suitable for passing as an
-        argument to vars().
-        @param name: A name for the namespace, which, if specified, provides
-        an additional level of indirection.
-        @type name: str
-        """
-        namespaces = object.__getattribute__(self, 'namespaces')
-        if name is None:
-            namespaces.append(ns)
-        else:
-            setattr(namespaces[0], name, ns)
-
-    def removeNamespace(self, ns, name=None):
-        """
-        Remove a namespace added with L{addNamespace}.
-        @param ns: The namespace to be removed.
-        @param name: The name which was specified when L{addNamespace} was
-        called.
-        @type name: str
-        """
-        namespaces = object.__getattribute__(self, 'namespaces')
-        if name is None:
-            namespaces.remove(ns)
-        else:
-            delattr(namespaces[0], name)
-
-    def __save__(self, stream, indent=0, no_close=False):
-        """
-        Save this configuration to the specified stream. The stream is
-        closed if this is the top-level configuration in the hierarchy.
-        L{Mapping.__save__} is called to do all the work.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output.
-        @type indent: int
-        """
-        Mapping.__save__(self, stream, indent)
-        if indent == 0 and not no_close:
-            stream.close()
-
-    def getByPath(self, path):
-        """
-        Obtain a value in the configuration via its path.
-        @param path: The path of the required value
-        @type path: str
-        @return the value at the specified path.
-        @rtype: any
-        @raise ConfigError: If the path is invalid
-        """
-        s = 'self.' + path
-        try:
-            return eval(s)
-        except Exception as e:
-            raise ConfigError(str(e))
-
-class Sequence(Container):
-    """
-    This internal class implements a value which is a sequence of other values.
-    """
-    class SeqIter(object):
-        """
-        This internal class implements an iterator for a L{Sequence} instance.
-        """
-        def __init__(self, seq):
-            self.seq = seq
-            self.limit = len(object.__getattribute__(seq, 'data'))
-            self.index = 0
-
-        def __iter__(self):
-            return self
-
-        def next(self):
-            if self.index >= self.limit:
-                raise StopIteration
-            rv = self.seq[self.index]
-            self.index += 1
-            return rv
-        
-        # This method is for python3 compatibility
-        def __next__(self): 
-            if self.index >= self.limit:
-                raise StopIteration
-            rv = self.seq[self.index]
-            self.index += 1
-            return rv
-
-    def __init__(self, parent=None):
-        """
-        Initialize an instance.
-
-        @param parent: The parent of this instance in the hierarchy.
-        @type parent: A L{Container} instance.
-        """
-        Container.__init__(self, parent)
-        object.__setattr__(self, 'data', [])
-        object.__setattr__(self, 'comments', [])
-
-    def append(self, item, comment):
-        """
-        Add an item to the sequence.
-
-        @param item: The item to add.
-        @type item: any
-        @param comment: A comment for the item.
-        @type comment: str
-        """
-        data = object.__getattribute__(self, 'data')
-        comments = object.__getattribute__(self, 'comments')
-        data.append(item)
-        comments.append(comment)
-
-    def __getitem__(self, index):
-        data = object.__getattribute__(self, 'data')
-        try:
-            rv = data[index]
-        except (IndexError, KeyError, TypeError):
-            raise ConfigResolutionError('%r is not a valid index for %r' % (index, object.__getattribute__(self, 'path')))
-        if not isinstance(rv, list):
-            rv = self.evaluate(rv)
-        else:
-            # deal with a slice
-            result = []
-            for a in rv:
-                result.append(self.evaluate(a))
-            rv = result
-        return rv
-
-    def __iter__(self):
-        return Sequence.SeqIter(self)
-
-    def __repr__(self):
-        return repr(object.__getattribute__(self, 'data'))
-
-    def __str__(self):
-        return str(self[:]) # using the slice evaluates the contents
-
-    def __len__(self):
-        return len(object.__getattribute__(self, 'data'))
-
-    def writeToStream(self, stream, indent, container):
-        """
-        Write this instance to a stream at the specified indentation level.
-
-        Should be redefined in subclasses.
-
-        @param stream: The stream to write to
-        @type stream: A writable stream (file-like object)
-        @param indent: The indentation level
-        @type indent: int
-        @param container: The container of this instance
-        @type container: L{Container}
-        """
-        indstr = indent * '  '
-        if len(self) == 0:
-            stream.write(' [ ]%s' % NEWLINE)
-        else:
-            if isinstance(container, Mapping):
-                stream.write(NEWLINE)
-            stream.write('%s[%s' % (indstr, NEWLINE))
-            self.__save__(stream, indent + 1)
-            stream.write('%s]%s' % (indstr, NEWLINE))
-
-    def __save__(self, stream, indent):
-        """
-        Save this instance to the specified stream.
-        @param stream: A stream to which the configuration is written.
-        @type stream: A write-only stream (file-like object).
-        @param indent: The indentation level for the output, > 0
-        @type indent: int
-        """
-        if indent == 0:
-            raise ConfigError("sequence cannot be saved as a top-level item")
-        data = object.__getattribute__(self, 'data')
-        comments = object.__getattribute__(self, 'comments')
-        indstr = indent * '  '
-        for i in xrange(0, len(data)):
-            value = data[i]
-            comment = comments[i]
-            if comment:
-                stream.write('%s#%s' % (indstr, comment))
-            if isinstance(value, Container):
-                value.writeToStream(stream, indent, self)
-            else:
-                self.writeValue(value, stream, indent)
-
-class Reference(object):
-    """
-    This internal class implements a value which is a reference to another value.
-    """
-    def __init__(self, config, type, ident):
-        """
-        Initialize an instance.
-
-        @param config: The configuration which contains this reference.
-        @type config: A L{Config} instance.
-        @param type: The type of reference.
-        @type type: BACKTICK or DOLLAR
-        @param ident: The identifier which starts the reference.
-        @type ident: str
-        """
-        self.config = config
-        self.type = type
-        self.elements = [ident]
-
-    def addElement(self, type, ident):
-        """
-        Add an element to the reference.
-
-        @param type: The type of reference.
-        @type type: BACKTICK or DOLLAR
-        @param ident: The identifier which continues the reference.
-        @type ident: str
-        """
-        self.elements.append((type, ident))
-
-    def findConfig(self, container):
-        """
-        Find the closest enclosing configuration to the specified container.
-
-        @param container: The container to start from.
-        @type container: L{Container}
-        @return: The closest enclosing configuration, or None.
-        @rtype: L{Config}
-        """
-        while (container is not None) and not isinstance(container, Config):
-            container = object.__getattribute__(container, 'parent')
-        return container
-
-    def resolve(self, container):
-        """
-        Resolve this instance in the context of a container.
-
-        @param container: The container to resolve from.
-        @type container: L{Container}
-        @return: The resolved value.
-        @rtype: any
-        @raise ConfigResolutionError: If resolution fails.
-        """
-        rv = None
-        path = object.__getattribute__(container, 'path')
-        current = container
-        while current is not None:
-            if self.type == BACKTICK:
-                namespaces = object.__getattribute__(current, 'namespaces')
-                found = False
-                for ns in namespaces:
-                    try:
-                        rv = eval(str(self)[1:-1], vars(ns))
-                        found = True
-                        break
-                    except:
-                        pass
-                if found:
-                    break
-            else:
-                key = self.elements[0]
-                try:
-                    rv = current[key]
-                    for item in self.elements[1:]:
-                        key = item[1]
-                        rv = rv[key]
-                    break
-                except:
-                    rv = None
-                    pass
-            current = object.__getattribute__(current, 'parent')
-        if current is None:
-            raise ConfigResolutionError("unable to evaluate %r in the configuration %s" % (self, path))
-        return rv
-
-    def __str__(self):
-        s = self.elements[0]
-        for tt, tv in self.elements[1:]:
-            if tt == DOT:
-                s += '.%s' % tv
-            else:
-                s += '[%r]' % tv
-        if self.type == BACKTICK:
-            return BACKTICK + s + BACKTICK
-        else:
-            return DOLLAR + s
-
-    def __repr__(self):
-        return self.__str__()
-
-class Expression(object):
-    """
-    This internal class implements a value which is obtained by evaluating an expression.
-    """
-    def __init__(self, op, lhs, rhs):
-        """
-        Initialize an instance.
-
-        @param op: the operation expressed in the expression.
-        @type op: PLUS, MINUS, STAR, SLASH, MOD
-        @param lhs: the left-hand-side operand of the expression.
-        @type lhs: any Expression or primary value.
-        @param rhs: the right-hand-side operand of the expression.
-        @type rhs: any Expression or primary value.
-        """
-        self.op = op
-        self.lhs = lhs
-        self.rhs = rhs
-
-    def __str__(self):
-        return '%r %s %r' % (self.lhs, self.op, self.rhs)
-
-    def __repr__(self):
-        return self.__str__()
-
-    def evaluate(self, container):
-        """
-        Evaluate this instance in the context of a container.
-
-        @param container: The container to evaluate in from.
-        @type container: L{Container}
-        @return: The evaluated value.
-        @rtype: any
-        @raise ConfigResolutionError: If evaluation fails.
-        @raise ZeroDivideError: If division by zero occurs.
-        @raise TypeError: If the operation is invalid, e.g.
-        subtracting one string from another.
-        """
-        lhs = self.lhs
-        if isinstance(lhs, Reference):
-            lhs = lhs.resolve(container)
-        elif isinstance(lhs, Expression):
-            lhs = lhs.evaluate(container)
-        rhs = self.rhs
-        if isinstance(rhs, Reference):
-            rhs = rhs.resolve(container)
-        elif isinstance(rhs, Expression):
-            rhs = rhs.evaluate(container)
-        op = self.op
-        if op == PLUS:
-            rv = lhs + rhs
-        elif op == MINUS:
-            rv = lhs - rhs
-        elif op == STAR:
-            rv = lhs * rhs
-        elif op == SLASH:
-            rv = lhs / rhs
-        else:
-            rv = lhs % rhs
-        return rv
-
-class ConfigReader(object):
-    """
-    This internal class implements a parser for configurations.
-    """
-
-    def __init__(self, config):
-        self.filename = None
-        self.config = config
-        self.lineno = 0
-        self.colno = 0
-        self.lastc = None
-        self.last_token = None
-        self.commentchars = '#'
-        self.whitespace = ' \t\r\n'
-        self.quotes = '\'"'
-        self.punct = ':-+*/%,.{}[]()@`$'
-        self.digits = '0123456789'
-        self.wordchars = '%s' % WORDCHARS # make a copy
-        self.identchars = self.wordchars + self.digits
-        self.pbchars = []
-        self.pbtokens = []
-        self.comment = None
-
-    def location(self):
-        """
-        Return the current location (filename, line, column) in the stream
-        as a string.
-
-        Used when printing error messages,
-
-        @return: A string representing a location in the stream being read.
-        @rtype: str
-        """
-        return "%s(%d,%d)" % (self.filename, self.lineno, self.colno)
-
-    def getChar(self):
-        """
-        Get the next char from the stream. Update line and column numbers
-        appropriately.
-
-        @return: The next character from the stream.
-        @rtype: str
-        """
-        if self.pbchars:
-            c = self.pbchars.pop()
-            if isinstance(c,bytes):
-                c = c.decode()
-        else:
-            c = self.stream.read(1)
-            if isinstance(c,bytes):
-                try:
-                    c = c.decode()
-                except:
-                    import pdb;pdb.set_trace()
-            self.colno += 1
-            if c == '\n':
-                self.lineno += 1
-                self.colno = 1
-        return c
-
-    def __repr__(self):
-        return "<ConfigReader at 0x%08x>" % id(self)
-
-    __str__ = __repr__
-
-    def getToken(self):
-        """
-        Get a token from the stream. String values are returned in a form
-        where you need to eval() the returned value to get the actual
-        string. The return value is (token_type, token_value).
-
-        Multiline string tokenizing is thanks to David Janes (BlogMatrix)
-
-        @return: The next token.
-        @rtype: A token tuple.
-        """
-        if self.pbtokens:
-            return self.pbtokens.pop()
-        stream = self.stream
-        self.comment = None
-        token = ''
-        tt = EOF
-        while True:
-            c = self.getChar()
-            if not c:
-                break
-            elif c == '#':
-                if self.comment :
-                    self.comment += '#' + stream.readline()
-                else :
-                    self.comment = stream.readline()
-                self.lineno += 1
-                continue
-            if c in self.quotes:
-                token = c
-                quote = c
-                tt = STRING
-                escaped = False
-                multiline = False
-                c1 = self.getChar()
-                if c1 == quote:
-                    c2 = self.getChar()
-                    if c2 == quote:
-                        multiline = True
-                        token += quote
-                        token += quote
-                    else:
-                        self.pbchars.append(c2)
-                        self.pbchars.append(c1)
-                else:
-                    self.pbchars.append(c1)
-                while True:
-                    c = self.getChar()
-                    if not c:
-                        break
-                    token += c
-                    if (c == quote) and not escaped:
-                        if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
-                            break
-                    if c == '\\':
-                        escaped = not escaped
-                    else:
-                        escaped = False
-                if not c:
-                    raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
-                break
-            if c in self.whitespace:
-                self.lastc = c
-                continue
-            elif c in self.punct:
-                token = c
-                tt = c
-                if (self.lastc == ']') or (self.lastc in self.identchars):
-                    if c == '[':
-                        tt = LBRACK2
-                    elif c == '(':
-                        tt = LPAREN2
-                break
-            elif c in self.digits:
-                token = c
-                tt = NUMBER
-                while True:
-                    c = self.getChar()
-                    if not c:
-                        break
-                    if c in self.digits:
-                        token += c
-                    elif (c == '.') and token.find('.') < 0:
-                        token += c
-                    else:
-                        if c and (c not in self.whitespace):
-                            self.pbchars.append(c)
-                        break
-                break
-            elif c in self.wordchars:
-                token = c
-                tt = WORD
-                c = self.getChar()
-                while c and (c in self.identchars):
-                    token += c
-                    c = self.getChar()
-                if c: # and c not in self.whitespace:
-                    self.pbchars.append(c)
-                if token == "True":
-                    tt = TRUE
-                elif token == "False":
-                    tt = FALSE
-                elif token == "None":
-                    tt = NONE
-                break
-            else:
-                raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
-        if token:
-            self.lastc = token[-1]
-        else:
-            self.lastc = None
-        self.last_token = tt
-        return (tt, token)
-
-    def load(self, stream, parent=None, suffix=None):
-        """
-        Load the configuration from the specified stream.
-
-        @param stream: A stream from which to load the configuration.
-        @type stream: A stream (file-like object).
-        @param parent: The parent of the configuration (to which this reader
-        belongs) in the hierarchy. Specified when the configuration is
-        included in another one.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix of this configuration in the parent
-        configuration. Should be specified whenever the parent is not None.
-        @raise ConfigError: If parent is specified but suffix is not.
-        @raise ConfigFormatError: If there are syntax errors in the stream.
-        """
-        if parent is not None:
-            if suffix is None:
-                raise ConfigError("internal error: load called with parent but no suffix")
-            self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
-        self.setStream(stream)
-        self.token = self.getToken()
-        self.parseMappingBody(self.config)
-        if self.token[0] != EOF:
-            raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
-
-    def setStream(self, stream):
-        """
-        Set the stream to the specified value, and prepare to read from it.
-
-        @param stream: A stream from which to load the configuration.
-        @type stream: A stream (file-like object).
-        """
-        self.stream = stream
-        if hasattr(stream, 'name'):
-            filename = stream.name
-        else:
-            filename = '?'
-        self.filename = filename
-        self.lineno = 1
-        self.colno = 1
-
-    def match(self, t):
-        """
-        Ensure that the current token type matches the specified value, and
-        advance to the next token.
-
-        @param t: The token type to match.
-        @type t: A valid token type.
-        @return: The token which was last read from the stream before this
-        function is called.
-        @rtype: a token tuple - see L{getToken}.
-        @raise ConfigFormatError: If the token does not match what's expected.
-        """
-        if self.token[0] != t:
-            raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
-        rv = self.token
-        self.token = self.getToken()
-        return rv
-
-    def parseMappingBody(self, parent):
-        """
-        Parse the internals of a mapping, and add entries to the provided
-        L{Mapping}.
-
-        @param parent: The mapping to add entries to.
-        @type parent: A L{Mapping} instance.
-        """
-        while self.token[0] in [WORD, STRING]:
-            self.parseKeyValuePair(parent)
-
-    def parseKeyValuePair(self, parent):
-        """
-        Parse a key-value pair, and add it to the provided L{Mapping}.
-
-        @param parent: The mapping to add entries to.
-        @type parent: A L{Mapping} instance.
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        comment = self.comment
-        tt, tv = self.token
-        if tt == WORD:
-            key = tv
-            suffix = tv
-        elif tt == STRING:
-            key = eval(tv)
-            suffix = '[%s]' % tv
-        else:
-            msg = "%s: expecting word or string, found %r"
-            raise ConfigFormatError(msg % (self.location(), tv))
-        self.token = self.getToken()
-        # for now, we allow key on its own as a short form of key : True
-        if self.token[0] == COLON:
-            self.token = self.getToken()
-            value = self.parseValue(parent, suffix)
-        else:
-            value = True
-        try:
-            parent.addMapping(key, value, comment)
-        except Exception as e:
-            raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
-                                    self.token[1]))
-        tt = self.token[0]
-        if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
-            msg = "%s: expecting one of EOF, WORD, STRING, \
-RCURLY, COMMA, found %r"
-            raise ConfigFormatError(msg  % (self.location(), self.token[1]))
-        if tt == COMMA:
-            self.token = self.getToken()
-
-    def parseValue(self, parent, suffix):
-        """
-        Parse a value.
-
-        @param parent: The container to which the value will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: The value
-        @rtype: any
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
-                  TRUE, FALSE, NONE, BACKTICK, MINUS]:
-            rv = self.parseScalar()
-        elif tt == LBRACK:
-            rv = self.parseSequence(parent, suffix)
-        elif tt in [LCURLY, AT]:
-            rv = self.parseMapping(parent, suffix)
-        else:
-            raise ConfigFormatError("%s: unexpected input: %r" %
-               (self.location(), self.token[1]))
-        return rv
-
-    def parseSequence(self, parent, suffix):
-        """
-        Parse a sequence.
-
-        @param parent: The container to which the sequence will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: a L{Sequence} instance representing the sequence.
-        @rtype: L{Sequence}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        rv = Sequence(parent)
-        rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
-        self.match(LBRACK)
-        comment = self.comment
-        tt = self.token[0]
-        while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
-                     TRUE, FALSE, NONE, BACKTICK]:
-            suffix = '[%d]' % len(rv)
-            value = self.parseValue(parent, suffix)
-            rv.append(value, comment)
-            tt = self.token[0]
-            comment = self.comment
-            if tt == COMMA:
-                self.match(COMMA)
-                tt = self.token[0]
-                comment = self.comment
-                continue
-        self.match(RBRACK)
-        return rv
-
-    def parseMapping(self, parent, suffix):
-        """
-        Parse a mapping.
-
-        @param parent: The container to which the mapping will be added.
-        @type parent: A L{Container} instance.
-        @param suffix: The suffix for the value.
-        @type suffix: str
-        @return: a L{Mapping} instance representing the mapping.
-        @rtype: L{Mapping}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        if self.token[0] == LCURLY:
-            self.match(LCURLY)
-            rv = Mapping(parent)
-            rv.setPath(
-               makePath(object.__getattribute__(parent, 'path'), suffix))
-            self.parseMappingBody(rv)
-            self.match(RCURLY)
-        else:
-            self.match(AT)
-            _, fn = self.match(STRING)
-            rv = Config(eval(fn), parent)
-        return rv
-
-    def parseScalar(self):
-        """
-        Parse a scalar - a terminal value such as a string or number, or
-        an L{Expression} or L{Reference}.
-
-        @return: the parsed scalar
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        lhs = self.parseTerm()
-        tt = self.token[0]
-        while tt in [PLUS, MINUS]:
-            self.match(tt)
-            rhs = self.parseTerm()
-            lhs = Expression(tt, lhs, rhs)
-            tt = self.token[0]
-        return lhs
-
-    def parseTerm(self):
-        """
-        Parse a term in an additive expression (a + b, a - b)
-
-        @return: the parsed term
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        lhs = self.parseFactor()
-        tt = self.token[0]
-        while tt in [STAR, SLASH, MOD]:
-            self.match(tt)
-            rhs = self.parseFactor()
-            lhs = Expression(tt, lhs, rhs)
-            tt = self.token[0]
-        return lhs
-
-    def parseFactor(self):
-        """
-        Parse a factor in an multiplicative expression (a * b, a / b, a % b)
-
-        @return: the parsed factor
-        @rtype: any scalar
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
-            rv = self.token[1]
-            if tt != WORD:
-                rv = eval(rv)
-            self.match(tt)
-        elif tt == LPAREN:
-            self.match(LPAREN)
-            rv = self.parseScalar()
-            self.match(RPAREN)
-        elif tt == DOLLAR:
-            self.match(DOLLAR)
-            rv = self.parseReference(DOLLAR)
-        elif tt == BACKTICK:
-            self.match(BACKTICK)
-            rv = self.parseReference(BACKTICK)
-            self.match(BACKTICK)
-        elif tt == MINUS:
-            self.match(MINUS)
-            rv = -self.parseScalar()
-        else:
-            raise ConfigFormatError("%s: unexpected input: %r" %
-               (self.location(), self.token[1]))
-        return rv
-
-    def parseReference(self, type):
-        """
-        Parse a reference.
-
-        @return: the parsed reference
-        @rtype: L{Reference}
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        word = self.match(WORD)
-        rv = Reference(self.config, type, word[1])
-        while self.token[0] in [DOT, LBRACK2]:
-            self.parseSuffix(rv)
-        return rv
-
-    def parseSuffix(self, ref):
-        """
-        Parse a reference suffix.
-
-        @param ref: The reference of which this suffix is a part.
-        @type ref: L{Reference}.
-        @raise ConfigFormatError: if a syntax error is found.
-        """
-        tt = self.token[0]
-        if tt == DOT:
-            self.match(DOT)
-            word = self.match(WORD)
-            ref.addElement(DOT, word[1])
-        else:
-            self.match(LBRACK2)
-            tt, tv = self.token
-            if tt not in [NUMBER, STRING]:
-                raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
-            self.token = self.getToken()
-            tv = eval(tv)
-            self.match(RBRACK)
-            ref.addElement(LBRACK, tv)
-
-def defaultMergeResolve(map1, map2, key):
-    """
-    A default resolver for merge conflicts. Returns a string
-    indicating what action to take to resolve the conflict.
-
-    @param map1: The map being merged into.
-    @type map1: L{Mapping}.
-    @param map2: The map being used as the merge operand.
-    @type map2: L{Mapping}.
-    @param key: The key in map2 (which also exists in map1).
-    @type key: str
-    @return: One of "merge", "append", "mismatch" or "overwrite"
-             indicating what action should be taken. This should
-             be appropriate to the objects being merged - e.g.
-             there is no point returning "merge" if the two objects
-             are instances of L{Sequence}.
-    @rtype: str
-    """
-    obj1 = map1[key]
-    obj2 = map2[key]
-    if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
-        rv = "merge"
-    elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
-        rv = "append"
-    else:
-        rv = "mismatch"
-    return rv
-
-def overwriteMergeResolve(map1, map2, key):
-    """
-    An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
-    but where a "mismatch" is detected, returns "overwrite" instead.
-
-    @param map1: The map being merged into.
-    @type map1: L{Mapping}.
-    @param map2: The map being used as the merge operand.
-    @type map2: L{Mapping}.
-    @param key: The key in map2 (which also exists in map1).
-    @type key: str
-    """
-    rv = defaultMergeResolve(map1, map2, key)
-    if rv == "mismatch":
-        rv = "overwrite"
-    return rv
-
-class ConfigMerger(object):
-    """
-    This class is used for merging two configurations. If a key exists in the
-    merge operand but not the merge target, then the entry is copied from the
-    merge operand to the merge target. If a key exists in both configurations,
-    then a resolver (a callable) is called to decide how to handle the
-    conflict.
-    """
-
-    def __init__(self, resolver=defaultMergeResolve):
-        """
-        Initialise an instance.
-
-        @param resolver:
-        @type resolver: A callable which takes the argument list
-        (map1, map2, key) where map1 is the mapping being merged into,
-        map2 is the merge operand and key is the clashing key. The callable
-        should return a string indicating how the conflict should be resolved.
-        For possible return values, see L{defaultMergeResolve}. The default
-        value preserves the old behaviour
-        """
-        self.resolver = resolver
-
-    def merge(self, merged, mergee):
-        """
-        Merge two configurations. The second configuration is unchanged,
-        and the first is changed to reflect the results of the merge.
-
-        @param merged: The configuration to merge into.
-        @type merged: L{Config}.
-        @param mergee: The configuration to merge.
-        @type mergee: L{Config}.
-        """
-        self.mergeMapping(merged, mergee)
-
-    def overwriteKeys(self, map1, seq2):
-        """
-        Renint variables. The second mapping is unchanged,
-        and the first is changed depending the keys of the second mapping.
-        @param map1: The mapping to reinit keys into.
-        @type map1: L{Mapping}.
-        @param map2: The mapping container reinit information.
-        @type map2: L{Mapping}.
-        """
-
-        overwrite_list = object.__getattribute__(seq2, 'data')
-        for overwrite_instruction in overwrite_list:
-            object.__setattr__(overwrite_instruction, 'parent', map1)
-            if "__condition__" in overwrite_instruction.keys():
-                overwrite_condition = overwrite_instruction["__condition__"]
-                if eval(overwrite_condition, globals(), map1):
-                    for key in overwrite_instruction.keys():
-                        if key == "__condition__":
-                            continue
-                        try:
-                            exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
-                        except:
-                            exec('map1.' + key + " = " + str(overwrite_instruction[key]))
-            else:
-                for key in overwrite_instruction.keys():
-                    try:
-                        exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
-                    except:
-                        exec('map1.' + key + " = " + str(overwrite_instruction[key]))
-
-    def mergeMapping(self, map1, map2):
-        """
-        Merge two mappings recursively. The second mapping is unchanged,
-        and the first is changed to reflect the results of the merge.
-
-        @param map1: The mapping to merge into.
-        @type map1: L{Mapping}.
-        @param map2: The mapping to merge.
-        @type map2: L{Mapping}.
-        """
-        keys = map1.keys()
-        global __resolveOverwrite__
-        for key in map2.keys():
-            if __resolveOverwrite__ and key == "__overwrite__":
-                self.overwriteKeys(map1,map2[key])
-
-            elif key not in keys:
-                map1[key] = map2[key]
-                if isinstance(map1[key], Container) :
-                    object.__setattr__(map1[key], 'parent', map1)
-            else:
-                obj1 = map1[key]
-                obj2 = map2[key]
-                decision = self.resolver(map1, map2, key)
-                if decision == "merge":
-                    self.mergeMapping(obj1, obj2)
-                elif decision == "append":
-                    self.mergeSequence(obj1, obj2)
-                elif decision == "overwrite":
-                    map1[key] = obj2
-                    if isinstance(map1[key], Container):
-                        object.__setattr__(map1[key], 'parent', map1)
-                elif decision == "mismatch":
-                    self.handleMismatch(obj1, obj2)
-                else:
-                    msg = "unable to merge: don't know how to implement %r"
-                    raise ValueError(msg % decision)
-
-    def mergeSequence(self, seq1, seq2):
-        """
-        Merge two sequences. The second sequence is unchanged,
-        and the first is changed to have the elements of the second
-        appended to it.
-
-        @param seq1: The sequence to merge into.
-        @type seq1: L{Sequence}.
-        @param seq2: The sequence to merge.
-        @type seq2: L{Sequence}.
-        """
-        data1 = object.__getattribute__(seq1, 'data')
-        data2 = object.__getattribute__(seq2, 'data')
-        for obj in data2:
-            data1.append(obj)
-        comment1 = object.__getattribute__(seq1, 'comments')
-        comment2 = object.__getattribute__(seq2, 'comments')
-        for obj in comment2:
-            comment1.append(obj)
-
-    def handleMismatch(self, obj1, obj2):
-        """
-        Handle a mismatch between two objects.
-
-        @param obj1: The object to merge into.
-        @type obj1: any
-        @param obj2: The object to merge.
-        @type obj2: any
-        """
-        raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
-
-class ConfigList(list):
-    """
-    This class implements an ordered list of configurations and allows you
-    to try getting the configuration from each entry in turn, returning
-    the first successfully obtained value.
-    """
-
-    def getByPath(self, path):
-        """
-        Obtain a value from the first configuration in the list which defines
-        it.
-
-        @param path: The path of the value to retrieve.
-        @type path: str
-        @return: The value from the earliest configuration in the list which
-        defines it.
-        @rtype: any
-        @raise ConfigError: If no configuration in the list has an entry with
-        the specified path.
-        """
-        found = False
-        rv = None
-        for entry in self:
-            try:
-                rv = entry.getByPath(path)
-                found = True
-                break
-            except ConfigError:
-                pass
-        if not found:
-            raise ConfigError("unable to resolve %r" % path)
-        return rv
diff --git a/src/common/fileSystem.py b/src/common/fileSystem.py
deleted file mode 100644 (file)
index cc8a39f..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2013  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-'''
-In this file : all functions that do a system call, like open a browser or an editor, or call a git command
-'''
-
-import sys
-import subprocess
-
-
-def show_in_editor(editor, filePath):
-    '''open filePath using editor.
-    
-    :param editor str: The editor to use.
-    :param filePath str: The path to the file to open.
-    '''
-    # default editor is vi
-    if editor is None or len(editor) == 0:
-        editor = 'vi'
-    
-    if '%s' not in editor:
-        editor += ' %s'
-
-    try:
-        # launch cmd using subprocess.Popen
-        cmd = editor % filePath
-        p = subprocess.Popen(cmd, shell=True)
-        p.communicate()
-    except:
-        sys.stderr.write("Unable to edit file %s\n" % filePath)
-    
\ No newline at end of file
diff --git a/src/common/pyconf.py b/src/common/pyconf.py
new file mode 100644 (file)
index 0000000..53bd184
--- /dev/null
@@ -0,0 +1,1704 @@
+#!/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, bytes):
+                global streamOpener
+                if streamOpener is None:
+                    streamOpener = defaultStreamOpener
+                streamOrFile = streamOpener(streamOrFile)
+            load = object.__getattribute__(self, "load")
+            load(streamOrFile)
+
+    def load(self, stream):
+        """
+        Load the configuration from the specified stream. Multiple streams can
+        be used to populate the same instance, as long as there are no
+        clashing keys. The stream is closed.
+        @param stream: A stream from which the configuration is read.
+        @type stream: A read-only stream (file-like object).
+        @raise ConfigError: if keys in the loaded configuration clash with
+        existing keys.
+        @raise ConfigFormatError: if there is a syntax error in the stream.
+        """
+        reader = object.__getattribute__(self, 'reader')
+        reader.load(stream)
+        stream.close()
+
+    def addNamespace(self, ns, name=None):
+        """
+        Add a namespace to this configuration which can be used to evaluate
+        (resolve) dotted-identifier expressions.
+        @param ns: The namespace to be added.
+        @type ns: A module or other namespace suitable for passing as an
+        argument to vars().
+        @param name: A name for the namespace, which, if specified, provides
+        an additional level of indirection.
+        @type name: str
+        """
+        namespaces = object.__getattribute__(self, 'namespaces')
+        if name is None:
+            namespaces.append(ns)
+        else:
+            setattr(namespaces[0], name, ns)
+
+    def removeNamespace(self, ns, name=None):
+        """
+        Remove a namespace added with L{addNamespace}.
+        @param ns: The namespace to be removed.
+        @param name: The name which was specified when L{addNamespace} was
+        called.
+        @type name: str
+        """
+        namespaces = object.__getattribute__(self, 'namespaces')
+        if name is None:
+            namespaces.remove(ns)
+        else:
+            delattr(namespaces[0], name)
+
+    def __save__(self, stream, indent=0, no_close=False):
+        """
+        Save this configuration to the specified stream. The stream is
+        closed if this is the top-level configuration in the hierarchy.
+        L{Mapping.__save__} is called to do all the work.
+        @param stream: A stream to which the configuration is written.
+        @type stream: A write-only stream (file-like object).
+        @param indent: The indentation level for the output.
+        @type indent: int
+        """
+        Mapping.__save__(self, stream, indent)
+        if indent == 0 and not no_close:
+            stream.close()
+
+    def getByPath(self, path):
+        """
+        Obtain a value in the configuration via its path.
+        @param path: The path of the required value
+        @type path: str
+        @return the value at the specified path.
+        @rtype: any
+        @raise ConfigError: If the path is invalid
+        """
+        s = 'self.' + path
+        try:
+            return eval(s)
+        except Exception as e:
+            raise ConfigError(str(e))
+
+class Sequence(Container):
+    """
+    This internal class implements a value which is a sequence of other values.
+    """
+    class SeqIter(object):
+        """
+        This internal class implements an iterator for a L{Sequence} instance.
+        """
+        def __init__(self, seq):
+            self.seq = seq
+            self.limit = len(object.__getattribute__(seq, 'data'))
+            self.index = 0
+
+        def __iter__(self):
+            return self
+
+        def next(self):
+            if self.index >= self.limit:
+                raise StopIteration
+            rv = self.seq[self.index]
+            self.index += 1
+            return rv
+        
+        # This method is for python3 compatibility
+        def __next__(self): 
+            if self.index >= self.limit:
+                raise StopIteration
+            rv = self.seq[self.index]
+            self.index += 1
+            return rv
+
+    def __init__(self, parent=None):
+        """
+        Initialize an instance.
+
+        @param parent: The parent of this instance in the hierarchy.
+        @type parent: A L{Container} instance.
+        """
+        Container.__init__(self, parent)
+        object.__setattr__(self, 'data', [])
+        object.__setattr__(self, 'comments', [])
+
+    def append(self, item, comment):
+        """
+        Add an item to the sequence.
+
+        @param item: The item to add.
+        @type item: any
+        @param comment: A comment for the item.
+        @type comment: str
+        """
+        data = object.__getattribute__(self, 'data')
+        comments = object.__getattribute__(self, 'comments')
+        data.append(item)
+        comments.append(comment)
+
+    def __getitem__(self, index):
+        data = object.__getattribute__(self, 'data')
+        try:
+            rv = data[index]
+        except (IndexError, KeyError, TypeError):
+            raise ConfigResolutionError('%r is not a valid index for %r' % (index, object.__getattribute__(self, 'path')))
+        if not isinstance(rv, list):
+            rv = self.evaluate(rv)
+        else:
+            # deal with a slice
+            result = []
+            for a in rv:
+                result.append(self.evaluate(a))
+            rv = result
+        return rv
+
+    def __iter__(self):
+        return Sequence.SeqIter(self)
+
+    def __repr__(self):
+        return repr(object.__getattribute__(self, 'data'))
+
+    def __str__(self):
+        return str(self[:]) # using the slice evaluates the contents
+
+    def __len__(self):
+        return len(object.__getattribute__(self, 'data'))
+
+    def writeToStream(self, stream, indent, container):
+        """
+        Write this instance to a stream at the specified indentation level.
+
+        Should be redefined in subclasses.
+
+        @param stream: The stream to write to
+        @type stream: A writable stream (file-like object)
+        @param indent: The indentation level
+        @type indent: int
+        @param container: The container of this instance
+        @type container: L{Container}
+        """
+        indstr = indent * '  '
+        if len(self) == 0:
+            stream.write(' [ ]%s' % NEWLINE)
+        else:
+            if isinstance(container, Mapping):
+                stream.write(NEWLINE)
+            stream.write('%s[%s' % (indstr, NEWLINE))
+            self.__save__(stream, indent + 1)
+            stream.write('%s]%s' % (indstr, NEWLINE))
+
+    def __save__(self, stream, indent):
+        """
+        Save this instance to the specified stream.
+        @param stream: A stream to which the configuration is written.
+        @type stream: A write-only stream (file-like object).
+        @param indent: The indentation level for the output, > 0
+        @type indent: int
+        """
+        if indent == 0:
+            raise ConfigError("sequence cannot be saved as a top-level item")
+        data = object.__getattribute__(self, 'data')
+        comments = object.__getattribute__(self, 'comments')
+        indstr = indent * '  '
+        for i in xrange(0, len(data)):
+            value = data[i]
+            comment = comments[i]
+            if comment:
+                stream.write('%s#%s' % (indstr, comment))
+            if isinstance(value, Container):
+                value.writeToStream(stream, indent, self)
+            else:
+                self.writeValue(value, stream, indent)
+
+class Reference(object):
+    """
+    This internal class implements a value which is a reference to another value.
+    """
+    def __init__(self, config, type, ident):
+        """
+        Initialize an instance.
+
+        @param config: The configuration which contains this reference.
+        @type config: A L{Config} instance.
+        @param type: The type of reference.
+        @type type: BACKTICK or DOLLAR
+        @param ident: The identifier which starts the reference.
+        @type ident: str
+        """
+        self.config = config
+        self.type = type
+        self.elements = [ident]
+
+    def addElement(self, type, ident):
+        """
+        Add an element to the reference.
+
+        @param type: The type of reference.
+        @type type: BACKTICK or DOLLAR
+        @param ident: The identifier which continues the reference.
+        @type ident: str
+        """
+        self.elements.append((type, ident))
+
+    def findConfig(self, container):
+        """
+        Find the closest enclosing configuration to the specified container.
+
+        @param container: The container to start from.
+        @type container: L{Container}
+        @return: The closest enclosing configuration, or None.
+        @rtype: L{Config}
+        """
+        while (container is not None) and not isinstance(container, Config):
+            container = object.__getattribute__(container, 'parent')
+        return container
+
+    def resolve(self, container):
+        """
+        Resolve this instance in the context of a container.
+
+        @param container: The container to resolve from.
+        @type container: L{Container}
+        @return: The resolved value.
+        @rtype: any
+        @raise ConfigResolutionError: If resolution fails.
+        """
+        rv = None
+        path = object.__getattribute__(container, 'path')
+        current = container
+        while current is not None:
+            if self.type == BACKTICK:
+                namespaces = object.__getattribute__(current, 'namespaces')
+                found = False
+                for ns in namespaces:
+                    try:
+                        rv = eval(str(self)[1:-1], vars(ns))
+                        found = True
+                        break
+                    except:
+                        pass
+                if found:
+                    break
+            else:
+                key = self.elements[0]
+                try:
+                    rv = current[key]
+                    for item in self.elements[1:]:
+                        key = item[1]
+                        rv = rv[key]
+                    break
+                except:
+                    rv = None
+                    pass
+            current = object.__getattribute__(current, 'parent')
+        if current is None:
+            raise ConfigResolutionError("unable to evaluate %r in the configuration %s" % (self, path))
+        return rv
+
+    def __str__(self):
+        s = self.elements[0]
+        for tt, tv in self.elements[1:]:
+            if tt == DOT:
+                s += '.%s' % tv
+            else:
+                s += '[%r]' % tv
+        if self.type == BACKTICK:
+            return BACKTICK + s + BACKTICK
+        else:
+            return DOLLAR + s
+
+    def __repr__(self):
+        return self.__str__()
+
+class Expression(object):
+    """
+    This internal class implements a value which is obtained by evaluating an expression.
+    """
+    def __init__(self, op, lhs, rhs):
+        """
+        Initialize an instance.
+
+        @param op: the operation expressed in the expression.
+        @type op: PLUS, MINUS, STAR, SLASH, MOD
+        @param lhs: the left-hand-side operand of the expression.
+        @type lhs: any Expression or primary value.
+        @param rhs: the right-hand-side operand of the expression.
+        @type rhs: any Expression or primary value.
+        """
+        self.op = op
+        self.lhs = lhs
+        self.rhs = rhs
+
+    def __str__(self):
+        return '%r %s %r' % (self.lhs, self.op, self.rhs)
+
+    def __repr__(self):
+        return self.__str__()
+
+    def evaluate(self, container):
+        """
+        Evaluate this instance in the context of a container.
+
+        @param container: The container to evaluate in from.
+        @type container: L{Container}
+        @return: The evaluated value.
+        @rtype: any
+        @raise ConfigResolutionError: If evaluation fails.
+        @raise ZeroDivideError: If division by zero occurs.
+        @raise TypeError: If the operation is invalid, e.g.
+        subtracting one string from another.
+        """
+        lhs = self.lhs
+        if isinstance(lhs, Reference):
+            lhs = lhs.resolve(container)
+        elif isinstance(lhs, Expression):
+            lhs = lhs.evaluate(container)
+        rhs = self.rhs
+        if isinstance(rhs, Reference):
+            rhs = rhs.resolve(container)
+        elif isinstance(rhs, Expression):
+            rhs = rhs.evaluate(container)
+        op = self.op
+        if op == PLUS:
+            rv = lhs + rhs
+        elif op == MINUS:
+            rv = lhs - rhs
+        elif op == STAR:
+            rv = lhs * rhs
+        elif op == SLASH:
+            rv = lhs / rhs
+        else:
+            rv = lhs % rhs
+        return rv
+
+class ConfigReader(object):
+    """
+    This internal class implements a parser for configurations.
+    """
+
+    def __init__(self, config):
+        self.filename = None
+        self.config = config
+        self.lineno = 0
+        self.colno = 0
+        self.lastc = None
+        self.last_token = None
+        self.commentchars = '#'
+        self.whitespace = ' \t\r\n'
+        self.quotes = '\'"'
+        self.punct = ':-+*/%,.{}[]()@`$'
+        self.digits = '0123456789'
+        self.wordchars = '%s' % WORDCHARS # make a copy
+        self.identchars = self.wordchars + self.digits
+        self.pbchars = []
+        self.pbtokens = []
+        self.comment = None
+
+    def location(self):
+        """
+        Return the current location (filename, line, column) in the stream
+        as a string.
+
+        Used when printing error messages,
+
+        @return: A string representing a location in the stream being read.
+        @rtype: str
+        """
+        return "%s(%d,%d)" % (self.filename, self.lineno, self.colno)
+
+    def getChar(self):
+        """
+        Get the next char from the stream. Update line and column numbers
+        appropriately.
+
+        @return: The next character from the stream.
+        @rtype: str
+        """
+        if self.pbchars:
+            c = self.pbchars.pop()
+            if isinstance(c,bytes):
+                c = c.decode()
+        else:
+            c = self.stream.read(1)
+            if isinstance(c,bytes):
+                try:
+                    c = c.decode()
+                except:
+                    import pdb;pdb.set_trace()
+            self.colno += 1
+            if c == '\n':
+                self.lineno += 1
+                self.colno = 1
+        return c
+
+    def __repr__(self):
+        return "<ConfigReader at 0x%08x>" % id(self)
+
+    __str__ = __repr__
+
+    def getToken(self):
+        """
+        Get a token from the stream. String values are returned in a form
+        where you need to eval() the returned value to get the actual
+        string. The return value is (token_type, token_value).
+
+        Multiline string tokenizing is thanks to David Janes (BlogMatrix)
+
+        @return: The next token.
+        @rtype: A token tuple.
+        """
+        if self.pbtokens:
+            return self.pbtokens.pop()
+        stream = self.stream
+        self.comment = None
+        token = ''
+        tt = EOF
+        while True:
+            c = self.getChar()
+            if not c:
+                break
+            elif c == '#':
+                if self.comment :
+                    self.comment += '#' + stream.readline()
+                else :
+                    self.comment = stream.readline()
+                self.lineno += 1
+                continue
+            if c in self.quotes:
+                token = c
+                quote = c
+                tt = STRING
+                escaped = False
+                multiline = False
+                c1 = self.getChar()
+                if c1 == quote:
+                    c2 = self.getChar()
+                    if c2 == quote:
+                        multiline = True
+                        token += quote
+                        token += quote
+                    else:
+                        self.pbchars.append(c2)
+                        self.pbchars.append(c1)
+                else:
+                    self.pbchars.append(c1)
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    token += c
+                    if (c == quote) and not escaped:
+                        if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
+                            break
+                    if c == '\\':
+                        escaped = not escaped
+                    else:
+                        escaped = False
+                if not c:
+                    raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
+                break
+            if c in self.whitespace:
+                self.lastc = c
+                continue
+            elif c in self.punct:
+                token = c
+                tt = c
+                if (self.lastc == ']') or (self.lastc in self.identchars):
+                    if c == '[':
+                        tt = LBRACK2
+                    elif c == '(':
+                        tt = LPAREN2
+                break
+            elif c in self.digits:
+                token = c
+                tt = NUMBER
+                while True:
+                    c = self.getChar()
+                    if not c:
+                        break
+                    if c in self.digits:
+                        token += c
+                    elif (c == '.') and token.find('.') < 0:
+                        token += c
+                    else:
+                        if c and (c not in self.whitespace):
+                            self.pbchars.append(c)
+                        break
+                break
+            elif c in self.wordchars:
+                token = c
+                tt = WORD
+                c = self.getChar()
+                while c and (c in self.identchars):
+                    token += c
+                    c = self.getChar()
+                if c: # and c not in self.whitespace:
+                    self.pbchars.append(c)
+                if token == "True":
+                    tt = TRUE
+                elif token == "False":
+                    tt = FALSE
+                elif token == "None":
+                    tt = NONE
+                break
+            else:
+                raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
+        if token:
+            self.lastc = token[-1]
+        else:
+            self.lastc = None
+        self.last_token = tt
+        return (tt, token)
+
+    def load(self, stream, parent=None, suffix=None):
+        """
+        Load the configuration from the specified stream.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        @param parent: The parent of the configuration (to which this reader
+        belongs) in the hierarchy. Specified when the configuration is
+        included in another one.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix of this configuration in the parent
+        configuration. Should be specified whenever the parent is not None.
+        @raise ConfigError: If parent is specified but suffix is not.
+        @raise ConfigFormatError: If there are syntax errors in the stream.
+        """
+        if parent is not None:
+            if suffix is None:
+                raise ConfigError("internal error: load called with parent but no suffix")
+            self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.setStream(stream)
+        self.token = self.getToken()
+        self.parseMappingBody(self.config)
+        if self.token[0] != EOF:
+            raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
+
+    def setStream(self, stream):
+        """
+        Set the stream to the specified value, and prepare to read from it.
+
+        @param stream: A stream from which to load the configuration.
+        @type stream: A stream (file-like object).
+        """
+        self.stream = stream
+        if hasattr(stream, 'name'):
+            filename = stream.name
+        else:
+            filename = '?'
+        self.filename = filename
+        self.lineno = 1
+        self.colno = 1
+
+    def match(self, t):
+        """
+        Ensure that the current token type matches the specified value, and
+        advance to the next token.
+
+        @param t: The token type to match.
+        @type t: A valid token type.
+        @return: The token which was last read from the stream before this
+        function is called.
+        @rtype: a token tuple - see L{getToken}.
+        @raise ConfigFormatError: If the token does not match what's expected.
+        """
+        if self.token[0] != t:
+            raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
+        rv = self.token
+        self.token = self.getToken()
+        return rv
+
+    def parseMappingBody(self, parent):
+        """
+        Parse the internals of a mapping, and add entries to the provided
+        L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        """
+        while self.token[0] in [WORD, STRING]:
+            self.parseKeyValuePair(parent)
+
+    def parseKeyValuePair(self, parent):
+        """
+        Parse a key-value pair, and add it to the provided L{Mapping}.
+
+        @param parent: The mapping to add entries to.
+        @type parent: A L{Mapping} instance.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        comment = self.comment
+        tt, tv = self.token
+        if tt == WORD:
+            key = tv
+            suffix = tv
+        elif tt == STRING:
+            key = eval(tv)
+            suffix = '[%s]' % tv
+        else:
+            msg = "%s: expecting word or string, found %r"
+            raise ConfigFormatError(msg % (self.location(), tv))
+        self.token = self.getToken()
+        # for now, we allow key on its own as a short form of key : True
+        if self.token[0] == COLON:
+            self.token = self.getToken()
+            value = self.parseValue(parent, suffix)
+        else:
+            value = True
+        try:
+            parent.addMapping(key, value, comment)
+        except Exception as e:
+            raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
+                                    self.token[1]))
+        tt = self.token[0]
+        if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
+            msg = "%s: expecting one of EOF, WORD, STRING, \
+RCURLY, COMMA, found %r"
+            raise ConfigFormatError(msg  % (self.location(), self.token[1]))
+        if tt == COMMA:
+            self.token = self.getToken()
+
+    def parseValue(self, parent, suffix):
+        """
+        Parse a value.
+
+        @param parent: The container to which the value will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: The value
+        @rtype: any
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
+                  TRUE, FALSE, NONE, BACKTICK, MINUS]:
+            rv = self.parseScalar()
+        elif tt == LBRACK:
+            rv = self.parseSequence(parent, suffix)
+        elif tt in [LCURLY, AT]:
+            rv = self.parseMapping(parent, suffix)
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseSequence(self, parent, suffix):
+        """
+        Parse a sequence.
+
+        @param parent: The container to which the sequence will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Sequence} instance representing the sequence.
+        @rtype: L{Sequence}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        rv = Sequence(parent)
+        rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
+        self.match(LBRACK)
+        comment = self.comment
+        tt = self.token[0]
+        while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
+                     TRUE, FALSE, NONE, BACKTICK]:
+            suffix = '[%d]' % len(rv)
+            value = self.parseValue(parent, suffix)
+            rv.append(value, comment)
+            tt = self.token[0]
+            comment = self.comment
+            if tt == COMMA:
+                self.match(COMMA)
+                tt = self.token[0]
+                comment = self.comment
+                continue
+        self.match(RBRACK)
+        return rv
+
+    def parseMapping(self, parent, suffix):
+        """
+        Parse a mapping.
+
+        @param parent: The container to which the mapping will be added.
+        @type parent: A L{Container} instance.
+        @param suffix: The suffix for the value.
+        @type suffix: str
+        @return: a L{Mapping} instance representing the mapping.
+        @rtype: L{Mapping}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        if self.token[0] == LCURLY:
+            self.match(LCURLY)
+            rv = Mapping(parent)
+            rv.setPath(
+               makePath(object.__getattribute__(parent, 'path'), suffix))
+            self.parseMappingBody(rv)
+            self.match(RCURLY)
+        else:
+            self.match(AT)
+            _, fn = self.match(STRING)
+            rv = Config(eval(fn), parent)
+        return rv
+
+    def parseScalar(self):
+        """
+        Parse a scalar - a terminal value such as a string or number, or
+        an L{Expression} or L{Reference}.
+
+        @return: the parsed scalar
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseTerm()
+        tt = self.token[0]
+        while tt in [PLUS, MINUS]:
+            self.match(tt)
+            rhs = self.parseTerm()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseTerm(self):
+        """
+        Parse a term in an additive expression (a + b, a - b)
+
+        @return: the parsed term
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        lhs = self.parseFactor()
+        tt = self.token[0]
+        while tt in [STAR, SLASH, MOD]:
+            self.match(tt)
+            rhs = self.parseFactor()
+            lhs = Expression(tt, lhs, rhs)
+            tt = self.token[0]
+        return lhs
+
+    def parseFactor(self):
+        """
+        Parse a factor in an multiplicative expression (a * b, a / b, a % b)
+
+        @return: the parsed factor
+        @rtype: any scalar
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
+            rv = self.token[1]
+            if tt != WORD:
+                rv = eval(rv)
+            self.match(tt)
+        elif tt == LPAREN:
+            self.match(LPAREN)
+            rv = self.parseScalar()
+            self.match(RPAREN)
+        elif tt == DOLLAR:
+            self.match(DOLLAR)
+            rv = self.parseReference(DOLLAR)
+        elif tt == BACKTICK:
+            self.match(BACKTICK)
+            rv = self.parseReference(BACKTICK)
+            self.match(BACKTICK)
+        elif tt == MINUS:
+            self.match(MINUS)
+            rv = -self.parseScalar()
+        else:
+            raise ConfigFormatError("%s: unexpected input: %r" %
+               (self.location(), self.token[1]))
+        return rv
+
+    def parseReference(self, type):
+        """
+        Parse a reference.
+
+        @return: the parsed reference
+        @rtype: L{Reference}
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        word = self.match(WORD)
+        rv = Reference(self.config, type, word[1])
+        while self.token[0] in [DOT, LBRACK2]:
+            self.parseSuffix(rv)
+        return rv
+
+    def parseSuffix(self, ref):
+        """
+        Parse a reference suffix.
+
+        @param ref: The reference of which this suffix is a part.
+        @type ref: L{Reference}.
+        @raise ConfigFormatError: if a syntax error is found.
+        """
+        tt = self.token[0]
+        if tt == DOT:
+            self.match(DOT)
+            word = self.match(WORD)
+            ref.addElement(DOT, word[1])
+        else:
+            self.match(LBRACK2)
+            tt, tv = self.token
+            if tt not in [NUMBER, STRING]:
+                raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
+            self.token = self.getToken()
+            tv = eval(tv)
+            self.match(RBRACK)
+            ref.addElement(LBRACK, tv)
+
+def defaultMergeResolve(map1, map2, key):
+    """
+    A default resolver for merge conflicts. Returns a string
+    indicating what action to take to resolve the conflict.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    @return: One of "merge", "append", "mismatch" or "overwrite"
+             indicating what action should be taken. This should
+             be appropriate to the objects being merged - e.g.
+             there is no point returning "merge" if the two objects
+             are instances of L{Sequence}.
+    @rtype: str
+    """
+    obj1 = map1[key]
+    obj2 = map2[key]
+    if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
+        rv = "merge"
+    elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
+        rv = "append"
+    else:
+        rv = "mismatch"
+    return rv
+
+def overwriteMergeResolve(map1, map2, key):
+    """
+    An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
+    but where a "mismatch" is detected, returns "overwrite" instead.
+
+    @param map1: The map being merged into.
+    @type map1: L{Mapping}.
+    @param map2: The map being used as the merge operand.
+    @type map2: L{Mapping}.
+    @param key: The key in map2 (which also exists in map1).
+    @type key: str
+    """
+    rv = defaultMergeResolve(map1, map2, key)
+    if rv == "mismatch":
+        rv = "overwrite"
+    return rv
+
+class ConfigMerger(object):
+    """
+    This class is used for merging two configurations. If a key exists in the
+    merge operand but not the merge target, then the entry is copied from the
+    merge operand to the merge target. If a key exists in both configurations,
+    then a resolver (a callable) is called to decide how to handle the
+    conflict.
+    """
+
+    def __init__(self, resolver=defaultMergeResolve):
+        """
+        Initialise an instance.
+
+        @param resolver:
+        @type resolver: A callable which takes the argument list
+        (map1, map2, key) where map1 is the mapping being merged into,
+        map2 is the merge operand and key is the clashing key. The callable
+        should return a string indicating how the conflict should be resolved.
+        For possible return values, see L{defaultMergeResolve}. The default
+        value preserves the old behaviour
+        """
+        self.resolver = resolver
+
+    def merge(self, merged, mergee):
+        """
+        Merge two configurations. The second configuration is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param merged: The configuration to merge into.
+        @type merged: L{Config}.
+        @param mergee: The configuration to merge.
+        @type mergee: L{Config}.
+        """
+        self.mergeMapping(merged, mergee)
+
+    def overwriteKeys(self, map1, seq2):
+        """
+        Renint variables. The second mapping is unchanged,
+        and the first is changed depending the keys of the second mapping.
+        @param map1: The mapping to reinit keys into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping container reinit information.
+        @type map2: L{Mapping}.
+        """
+
+        overwrite_list = object.__getattribute__(seq2, 'data')
+        for overwrite_instruction in overwrite_list:
+            object.__setattr__(overwrite_instruction, 'parent', map1)
+            if "__condition__" in overwrite_instruction.keys():
+                overwrite_condition = overwrite_instruction["__condition__"]
+                if eval(overwrite_condition, globals(), map1):
+                    for key in overwrite_instruction.keys():
+                        if key == "__condition__":
+                            continue
+                        try:
+                            exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
+                        except:
+                            exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+            else:
+                for key in overwrite_instruction.keys():
+                    try:
+                        exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
+                    except:
+                        exec('map1.' + key + " = " + str(overwrite_instruction[key]))
+
+    def mergeMapping(self, map1, map2):
+        """
+        Merge two mappings recursively. The second mapping is unchanged,
+        and the first is changed to reflect the results of the merge.
+
+        @param map1: The mapping to merge into.
+        @type map1: L{Mapping}.
+        @param map2: The mapping to merge.
+        @type map2: L{Mapping}.
+        """
+        keys = map1.keys()
+        global __resolveOverwrite__
+        for key in map2.keys():
+            if __resolveOverwrite__ and key == "__overwrite__":
+                self.overwriteKeys(map1,map2[key])
+
+            elif key not in keys:
+                map1[key] = map2[key]
+                if isinstance(map1[key], Container) :
+                    object.__setattr__(map1[key], 'parent', map1)
+            else:
+                obj1 = map1[key]
+                obj2 = map2[key]
+                decision = self.resolver(map1, map2, key)
+                if decision == "merge":
+                    self.mergeMapping(obj1, obj2)
+                elif decision == "append":
+                    self.mergeSequence(obj1, obj2)
+                elif decision == "overwrite":
+                    map1[key] = obj2
+                    if isinstance(map1[key], Container):
+                        object.__setattr__(map1[key], 'parent', map1)
+                elif decision == "mismatch":
+                    self.handleMismatch(obj1, obj2)
+                else:
+                    msg = "unable to merge: don't know how to implement %r"
+                    raise ValueError(msg % decision)
+
+    def mergeSequence(self, seq1, seq2):
+        """
+        Merge two sequences. The second sequence is unchanged,
+        and the first is changed to have the elements of the second
+        appended to it.
+
+        @param seq1: The sequence to merge into.
+        @type seq1: L{Sequence}.
+        @param seq2: The sequence to merge.
+        @type seq2: L{Sequence}.
+        """
+        data1 = object.__getattribute__(seq1, 'data')
+        data2 = object.__getattribute__(seq2, 'data')
+        for obj in data2:
+            data1.append(obj)
+        comment1 = object.__getattribute__(seq1, 'comments')
+        comment2 = object.__getattribute__(seq2, 'comments')
+        for obj in comment2:
+            comment1.append(obj)
+
+    def handleMismatch(self, obj1, obj2):
+        """
+        Handle a mismatch between two objects.
+
+        @param obj1: The object to merge into.
+        @type obj1: any
+        @param obj2: The object to merge.
+        @type obj2: any
+        """
+        raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
+
+class ConfigList(list):
+    """
+    This class implements an ordered list of configurations and allows you
+    to try getting the configuration from each entry in turn, returning
+    the first successfully obtained value.
+    """
+
+    def getByPath(self, path):
+        """
+        Obtain a value from the first configuration in the list which defines
+        it.
+
+        @param path: The path of the value to retrieve.
+        @type path: str
+        @return: The value from the earliest configuration in the list which
+        defines it.
+        @rtype: any
+        @raise ConfigError: If no configuration in the list has an entry with
+        the specified path.
+        """
+        found = False
+        rv = None
+        for entry in self:
+            try:
+                rv = entry.getByPath(path)
+                found = True
+                break
+            except ConfigError:
+                pass
+        if not found:
+            raise ConfigError("unable to resolve %r" % path)
+        return rv
diff --git a/src/common/system.py b/src/common/system.py
new file mode 100644 (file)
index 0000000..cc8a39f
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+#  Copyright (C) 2010-2013  CEA/DEN
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU Lesser General Public
+#  License as published by the Free Software Foundation; either
+#  version 2.1 of the License.
+#
+#  This library is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  Lesser General Public License for more details.
+#
+#  You should have received a copy of the GNU Lesser General Public
+#  License along with this library; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+'''
+In this file : all functions that do a system call, like open a browser or an editor, or call a git command
+'''
+
+import sys
+import subprocess
+
+
+def show_in_editor(editor, filePath):
+    '''open filePath using editor.
+    
+    :param editor str: The editor to use.
+    :param filePath str: The path to the file to open.
+    '''
+    # default editor is vi
+    if editor is None or len(editor) == 0:
+        editor = 'vi'
+    
+    if '%s' not in editor:
+        editor += ' %s'
+
+    try:
+        # launch cmd using subprocess.Popen
+        cmd = editor % filePath
+        p = subprocess.Popen(cmd, shell=True)
+        p.communicate()
+    except:
+        sys.stderr.write("Unable to edit file %s\n" % filePath)
+    
\ No newline at end of file
index 9a9bec5cd10f894e78e80b41a1d8137450dff0bb..5f18159fb14e85d49019ab694991c0155223680a 100644 (file)
@@ -53,9 +53,9 @@ class ConfigOpener:
 
     def __call__(self, name):
         if os.path.isabs(name):
-            return common.config_pyconf.ConfigInputStream(open(name, 'rb'))
+            return common.pyconf.ConfigInputStream(open(name, 'rb'))
         else:
-            return common.config_pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') )
+            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 ):
@@ -96,7 +96,7 @@ class ConfigManager:
         var['personalDir'] = os.path.join(os.path.expanduser('~'), '.salomeTools')
 
         # read linux distributions dictionary
-        distrib_cfg = common.config_pyconf.Config(os.path.join(var['srcDir'], 'common', 'internal_config', 'distrib.pyconf'))
+        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)
@@ -168,20 +168,20 @@ class ConfigManager:
         :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'
+        :rtype: class 'common.pyconf.Config'
         '''        
         
         # create a ConfigMerger to handle merge
-        merger = common.config_pyconf.ConfigMerger()#MergeHandler())
+        merger = common.pyconf.ConfigMerger()#MergeHandler())
         
         # create the configuration instance
-        cfg = common.config_pyconf.Config()
+        cfg = common.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)
+        cfg.VARS = common.pyconf.Mapping(cfg)
         for variable in var:
             cfg.VARS[variable] = var[variable]
         
@@ -192,10 +192,10 @@ class ConfigManager:
         # =======================================================================================
         # Load INTERNAL config
         # read src/common/internal_config/salomeTools.pyconf
-        common.config_pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'common', 'internal_config')])
+        common.pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'common', 'internal_config')])
         try:
-            internal_cfg = common.config_pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'common', 'internal_config', 'salomeTools.pyconf')))
-        except common.config_pyconf.ConfigError as e:
+            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) })
         
@@ -208,10 +208,10 @@ class ConfigManager:
         # =======================================================================================
         # Load SITE config file
         # search only in the data directory
-        common.config_pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir])
+        common.pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir])
         try:
-            site_cfg = common.config_pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf')))
-        except common.config_pyconf.ConfigError as e:
+            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:
@@ -235,12 +235,12 @@ class ConfigManager:
         if application is not None:
             # search APPLICATION file in all directories in configPath
             cp = cfg.SITE.config.configPath
-            common.config_pyconf.streamOpener = ConfigOpener(cp)
+            common.pyconf.streamOpener = ConfigOpener(cp)
             try:
-                application_cfg = common.config_pyconf.Config(application + '.pyconf')
+                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.config_pyconf.ConfigError as 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) } )
 
@@ -254,15 +254,15 @@ class ConfigManager:
         # Load softwares config files in SOFTWARE section
        
         # The directory containing the softwares definition
-        softsDir = os.path.join(cfg.VARS.dataDir, 'software_pyconf')
+        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.config_pyconf.streamOpener = ConfigOpener([softsDir])
+                common.pyconf.streamOpener = ConfigOpener([softsDir])
                 try:
-                    soft_cfg = common.config_pyconf.Config(open(os.path.join(softsDir, fName)))
-                except common.config_pyconf.ConfigError as e:
+                    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:
@@ -280,7 +280,7 @@ class ConfigManager:
         # load USER config
         self.setUserConfigFile(cfg)
         user_cfg_file = self.getUserConfigFile()
-        user_cfg = common.config_pyconf.Config(open(user_cfg_file))
+        user_cfg = common.pyconf.Config(open(user_cfg_file))
         merger.merge(cfg, user_cfg)
 
         # apply overwrite from command line if needed
@@ -293,7 +293,7 @@ class ConfigManager:
         '''Set the user config file name and path.
         If necessary, build it from another one or create it from scratch.
         
-        :param config class 'common.config_pyconf.Config': The global config (containing all pyconf).
+        :param config class 'common.pyconf.Config': The global config (containing all pyconf).
         '''
         if not config:
             raise common.SatException(_("Error in setUserConfigFile: config is None"))
@@ -347,16 +347,16 @@ class ConfigManager:
     def createConfigFile(self, config):
         '''This method is called when there are no user config file. It build it from scratch.
         
-        :param config class 'common.config_pyconf.Config': The global config.
+        :param config class 'common.pyconf.Config': The global config.
         :return: the config corresponding to the file created.
-        :rtype: config class 'common.config_pyconf.Config'
+        :rtype: config class 'common.pyconf.Config'
         '''
         
         cfg_name = self.getUserConfigFile()
 
-        user_cfg = common.config_pyconf.Config()
+        user_cfg = common.pyconf.Config()
         #
-        user_cfg.addMapping('USER', common.config_pyconf.Mapping(user_cfg), "")
+        user_cfg.addMapping('USER', common.pyconf.Mapping(user_cfg), "")
 
         #
         user_cfg.USER.addMapping('workDir', os.path.expanduser('~'),
@@ -395,7 +395,7 @@ class ConfigManager:
 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 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.
@@ -429,7 +429,7 @@ def print_value(config, path, show_label, level=0, show_full_path=False):
         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)
+    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:
@@ -468,13 +468,13 @@ def run(args, runner):
         editor = runner.cfg.USER.editor
         if 'APPLICATION' not in runner.cfg: # edit user pyconf
             usercfg = os.path.join(runner.cfg.VARS.personalDir, 'salomeTools-%s.pyconf'%runner.cfg.INTERNAL['sat_version'])
-            common.fileSystem.show_in_editor(editor, usercfg)
+            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.fileSystem.show_in_editor(editor, pyconf_path)
+                    common.system.show_in_editor(editor, pyconf_path)
                     break
     
     # case : copy an existing <application>.pyconf to ~/.salomeTools/Applications/LOCAL_<application>.pyconf
index 8fcf24430626cc1b21f649fe9051bc2552c7acfd..a120254b4cc5acd13c8cf73e63a7a54bf8ed9d5b 100755 (executable)
@@ -47,7 +47,7 @@ def find_command_list(dirPath):
     '''
     cmd_list = []
     for item in os.listdir(dirPath):
-        if item.endswith('.py') and item!='salomeTools.py':
+        if item.endswith('.py') and item!='salomeTools.py' and item!='__init__.py':
             cmd_list.append(item[:-len('.py')])
     return cmd_list
 
@@ -64,7 +64,7 @@ parser.add_option('g', 'debug', 'boolean', 'debug_mode', _("run salomeTools in d
 class salomeTools(object):
     '''The main class that stores all the commands of salomeTools
     '''
-    def __init__(self, opt, dataDir=None):
+    def __init__(self, opt='', dataDir=None):
         '''Initialization
         
         :param opt str: The sat options 
@@ -79,7 +79,7 @@ class salomeTools(object):
 
         # initialization of class attributes       
         self.__dict__ = dict()
-        self.cfg = None # the config that will be read using config_pyconf module
+        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
@@ -115,7 +115,7 @@ class salomeTools(object):
             (file_, pathname, description) = imp.find_module(nameCmd, [dirPath])
             module = imp.load_module(nameCmd, file_, pathname, description)
             
-            def run_command(args):
+            def run_command(args=''):
                 '''The function that will load the configuration (all pyconf)
                 and return the function run of the command corresponding to module
                 
diff --git a/test/__TOOLS__/HTMLTestRunner.py b/test/__TOOLS__/HTMLTestRunner.py
deleted file mode 100644 (file)
index 0439bf4..0000000
+++ /dev/null
@@ -1,824 +0,0 @@
-"""
-A TestRunner for use with the Python unit testing framework. It
-generates a HTML report to show the result at a glance.
-
-The simplest way to use this is to invoke its main method. E.g.
-
-    import unittest
-    import HTMLTestRunner
-
-    ... define your tests ...
-
-    if __name__ == '__main__':
-        HTMLTestRunner.main()
-
-
-For more customization options, instantiates a HTMLTestRunner object.
-HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
-
-    # output to a file
-    fp = file('my_report.html', 'wb')
-    runner = HTMLTestRunner.HTMLTestRunner(
-                stream=fp,
-                title='My unit test',
-                description='This demonstrates the report output by HTMLTestRunner.'
-                )
-
-    # Use an external stylesheet.
-    # See the Template_mixin class for more customizable options
-    runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
-
-    # run the test
-    runner.run(my_test_suite)
-
-
-------------------------------------------------------------------------
-Copyright (c) 2004-2007, Wai Yip Tung
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-* Redistributions of source code must retain the above copyright notice,
-  this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright
-  notice, this list of conditions and the following disclaimer in the
-  documentation and/or other materials provided with the distribution.
-* Neither the name Wai Yip Tung nor the names of its contributors may be
-  used to endorse or promote products derived from this software without
-  specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-# URL: http://tungwaiyip.info/software/HTMLTestRunner.html
-
-__author__ = "Wai Yip Tung"
-__version__ = "0.8.2"
-
-
-"""
-Change History
-
-Version 0.8.2
-* Show output inline instead of popup window (Viorel Lupu).
-
-Version in 0.8.1
-* Validated XHTML (Wolfgang Borgert).
-* Added description of test classes and test cases.
-
-Version in 0.8.0
-* Define Template_mixin class for customization.
-* Workaround a IE 6 bug that it does not treat <script> block as CDATA.
-
-Version in 0.7.1
-* Back port to Python 2.3 (Frank Horowitz).
-* Fix missing scroll bars in detail log (Podi).
-"""
-
-# TODO: color stderr
-# TODO: simplify javascript using ,ore than 1 class in the class attribute?
-
-import datetime
-import StringIO
-import sys
-import time
-import unittest
-from xml.sax import saxutils
-
-
-# ------------------------------------------------------------------------
-# The redirectors below are used to capture output during testing. Output
-# sent to sys.stdout and sys.stderr are automatically captured. However
-# in some cases sys.stdout is already cached before HTMLTestRunner is
-# invoked (e.g. calling logging.basicConfig). In order to capture those
-# output, use the redirectors for the cached stream.
-#
-# e.g.
-#   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
-#   >>>
-
-class OutputRedirector(object):
-    """ Wrapper to redirect stdout or stderr """
-    def __init__(self, fp):
-        self.fp = fp
-
-    def write(self, s):
-        self.fp.write(s)
-
-    def writelines(self, lines):
-        self.fp.writelines(lines)
-
-    def flush(self):
-        self.fp.flush()
-
-stdout_redirector = OutputRedirector(sys.stdout)
-stderr_redirector = OutputRedirector(sys.stderr)
-
-
-
-# ----------------------------------------------------------------------
-# Template
-
-class Template_mixin(object):
-    """
-    Define a HTML template for report customerization and generation.
-
-    Overall structure of an HTML report
-
-    HTML
-    +------------------------+
-    |<html>                  |
-    |  <head>                |
-    |                        |
-    |   STYLESHEET           |
-    |   +----------------+   |
-    |   |                |   |
-    |   +----------------+   |
-    |                        |
-    |  </head>               |
-    |                        |
-    |  <body>                |
-    |                        |
-    |   HEADING              |
-    |   +----------------+   |
-    |   |                |   |
-    |   +----------------+   |
-    |                        |
-    |   REPORT               |
-    |   +----------------+   |
-    |   |                |   |
-    |   +----------------+   |
-    |                        |
-    |   ENDING               |
-    |   +----------------+   |
-    |   |                |   |
-    |   +----------------+   |
-    |                        |
-    |  </body>               |
-    |</html>                 |
-    +------------------------+
-    """
-
-    STATUS = {
-    0: 'pass',
-    1: 'fail',
-    2: 'error',
-    }
-
-    DEFAULT_TITLE = 'Unit Test Report'
-    DEFAULT_DESCRIPTION = ''
-
-    # ------------------------------------------------------------------------
-    # HTML Template
-
-    HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-    <title>%(title)s</title>
-    <meta name="generator" content="%(generator)s"/>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-    %(stylesheet)s
-</head>
-<body>
-<script language="javascript" type="text/javascript"><!--
-output_list = Array();
-
-/* level - 0:Summary; 1:Failed; 2:All */
-function showCase(level) {
-    trs = document.getElementsByTagName("tr");
-    for (var i = 0; i < trs.length; i++) {
-        tr = trs[i];
-        id = tr.id;
-        if (id.substr(0,2) == 'ft') {
-            if (level < 1) {
-                tr.className = 'hiddenRow';
-            }
-            else {
-                tr.className = '';
-            }
-        }
-        if (id.substr(0,2) == 'pt') {
-            if (level > 1) {
-                tr.className = '';
-            }
-            else {
-                tr.className = 'hiddenRow';
-            }
-        }
-    }
-}
-
-
-function showClassDetail(cid, count) {
-    var id_list = Array(count);
-    var toHide = 1;
-    for (var i = 0; i < count; i++) {
-        tid0 = 't' + cid.substr(1) + '.' + (i+1);
-        tid = 'f' + tid0;
-        tr = document.getElementById(tid);
-        if (!tr) {
-            tid = 'p' + tid0;
-            tr = document.getElementById(tid);
-        }
-        id_list[i] = tid;
-        if (tr.className) {
-            toHide = 0;
-        }
-    }
-    for (var i = 0; i < count; i++) {
-        tid = id_list[i];
-        if (toHide) {
-            document.getElementById('div_'+tid).style.display = 'none'
-            document.getElementById(tid).className = 'hiddenRow';
-        }
-        else {
-            document.getElementById(tid).className = '';
-        }
-    }
-}
-
-
-function showTestDetail(div_id){
-    var details_div = document.getElementById(div_id)
-    var displayState = details_div.style.display
-    // alert(displayState)
-    if (displayState != 'block' ) {
-        displayState = 'block'
-        details_div.style.display = 'block'
-    }
-    else {
-        details_div.style.display = 'none'
-    }
-}
-
-
-function html_escape(s) {
-    s = s.replace(/&/g,'&amp;');
-    s = s.replace(/</g,'&lt;');
-    s = s.replace(/>/g,'&gt;');
-    return s;
-}
-
-/* obsoleted by detail in <div>
-function showOutput(id, name) {
-    var w = window.open("", //url
-                    name,
-                    "resizable,scrollbars,status,width=800,height=450");
-    d = w.document;
-    d.write("<pre>");
-    d.write(html_escape(output_list[id]));
-    d.write("\n");
-    d.write("<a href='javascript:window.close()'>close</a>\n");
-    d.write("</pre>\n");
-    d.close();
-}
-*/
---></script>
-
-%(heading)s
-%(report)s
-%(ending)s
-
-</body>
-</html>
-"""
-    # variables: (title, generator, stylesheet, heading, report, ending)
-
-
-    # ------------------------------------------------------------------------
-    # Stylesheet
-    #
-    # alternatively use a <link> for external style sheet, e.g.
-    #   <link rel="stylesheet" href="$url" type="text/css">
-
-    STYLESHEET_TMPL = """
-<style type="text/css" media="screen">
-body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
-table       { font-size: 100%; }
-pre         { }
-
-/* -- heading ---------------------------------------------------------------------- */
-h1 {
-       font-size: 16pt;
-       color: gray;
-}
-.heading {
-    margin-top: 0ex;
-    margin-bottom: 1ex;
-}
-
-.heading .attribute {
-    margin-top: 1ex;
-    margin-bottom: 0;
-}
-
-.heading .description {
-    margin-top: 4ex;
-    margin-bottom: 6ex;
-}
-
-/* -- css div popup ------------------------------------------------------------------------ */
-a.popup_link {
-}
-
-a.popup_link:hover {
-    color: red;
-}
-
-.popup_window {
-    display: none;
-    position: relative;
-    left: 0px;
-    top: 0px;
-    /*border: solid #627173 1px; */
-    padding: 10px;
-    background-color: #E6E6D6;
-    font-family: "Lucida Console", "Courier New", Courier, monospace;
-    text-align: left;
-    font-size: 8pt;
-    width: 500px;
-}
-
-}
-/* -- report ------------------------------------------------------------------------ */
-#show_detail_line {
-    margin-top: 3ex;
-    margin-bottom: 1ex;
-}
-#result_table {
-    width: 80%;
-    border-collapse: collapse;
-    border: 1px solid #777;
-}
-#header_row {
-    font-weight: bold;
-    color: white;
-    background-color: #777;
-}
-#result_table td {
-    border: 1px solid #777;
-    padding: 2px;
-}
-#total_row  { font-weight: bold; }
-.passClass  { background-color: #6c6; }
-.failClass  { background-color: #c60; }
-.errorClass { background-color: #c00; }
-.passCase   { color: #6c6; }
-.failCase   { color: #c60; font-weight: bold; }
-.errorCase  { color: #c00; font-weight: bold; }
-.hiddenRow  { display: none; }
-.testcase   { margin-left: 2em; }
-
-
-/* -- ending ---------------------------------------------------------------------- */
-#ending {
-}
-
-</style>
-"""
-
-
-
-    # ------------------------------------------------------------------------
-    # Heading
-    #
-
-    HEADING_TMPL = """<div class='heading'>
-<h1>%(title)s</h1>
-%(parameters)s
-<p class='description'>%(description)s</p>
-</div>
-
-""" # variables: (title, parameters, description)
-
-    HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
-""" # variables: (name, value)
-
-
-
-    # ------------------------------------------------------------------------
-    # Report
-    #
-
-    REPORT_TMPL = """
-<p id='show_detail_line'>Show
-<a href='javascript:showCase(0)'>Summary</a>
-<a href='javascript:showCase(1)'>Failed</a>
-<a href='javascript:showCase(2)'>All</a>
-</p>
-<table id='result_table'>
-<colgroup>
-<col align='left' />
-<col align='right' />
-<col align='right' />
-<col align='right' />
-<col align='right' />
-<col align='right' />
-</colgroup>
-<tr id='header_row'>
-    <td>Test Group/Test case</td>
-    <td>Count</td>
-    <td>Pass</td>
-    <td>Fail</td>
-    <td>Error</td>
-    <td>View</td>
-</tr>
-%(test_list)s
-<tr id='total_row'>
-    <td>Total</td>
-    <td>%(count)s</td>
-    <td>%(Pass)s</td>
-    <td>%(fail)s</td>
-    <td>%(error)s</td>
-    <td>&nbsp;</td>
-</tr>
-</table>
-""" # variables: (test_list, count, Pass, fail, error)
-
-    REPORT_CLASS_TMPL = r"""
-<tr class='%(style)s'>
-    <td>%(desc)s</td>
-    <td>%(count)s</td>
-    <td>%(Pass)s</td>
-    <td>%(fail)s</td>
-    <td>%(error)s</td>
-    <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
-</tr>
-""" # variables: (style, desc, count, Pass, fail, error, cid)
-
-
-    REPORT_TEST_WITH_OUTPUT_TMPL = r"""
-<tr id='%(tid)s' class='%(Class)s'>
-    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
-    <td colspan='5' align='center'>
-
-    <!--css div popup start-->
-    <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
-        %(status)s</a>
-
-    <div id='div_%(tid)s' class="popup_window">
-        <div style='text-align: right; color:red;cursor:pointer'>
-        <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
-           [x]</a>
-        </div>
-        <pre>
-        %(script)s
-        </pre>
-    </div>
-    <!--css div popup end-->
-
-    </td>
-</tr>
-""" # variables: (tid, Class, style, desc, status)
-
-
-    REPORT_TEST_NO_OUTPUT_TMPL = r"""
-<tr id='%(tid)s' class='%(Class)s'>
-    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
-    <td colspan='5' align='center'>%(status)s</td>
-</tr>
-""" # variables: (tid, Class, style, desc, status)
-
-
-    REPORT_TEST_OUTPUT_TMPL = r"""
-%(id)s: %(output)s
-""" # variables: (id, output)
-
-
-
-    # ------------------------------------------------------------------------
-    # ENDING
-    #
-
-    ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
-
-# -------------------- The end of the Template class -------------------
-
-
-TestResult = unittest.TestResult
-
-class _TestResult(TestResult):
-    # note: _TestResult is a pure representation of results.
-    # It lacks the output and reporting ability compares to unittest._TextTestResult.
-
-    def __init__(self, verbosity=1):
-        TestResult.__init__(self)
-        self.stdout0 = None
-        self.stderr0 = None
-        self.success_count = 0
-        self.failure_count = 0
-        self.error_count = 0
-        self.verbosity = verbosity
-
-        # result is a list of result in 4 tuple
-        # (
-        #   result code (0: success; 1: fail; 2: error),
-        #   TestCase object,
-        #   Test output (byte string),
-        #   stack trace,
-        # )
-        self.result = []
-
-
-    def startTest(self, test):
-        TestResult.startTest(self, test)
-        # just one buffer for both stdout and stderr
-        self.outputBuffer = StringIO.StringIO()
-        stdout_redirector.fp = self.outputBuffer
-        stderr_redirector.fp = self.outputBuffer
-        self.stdout0 = sys.stdout
-        self.stderr0 = sys.stderr
-        sys.stdout = stdout_redirector
-        sys.stderr = stderr_redirector
-
-
-    def complete_output(self):
-        """
-        Disconnect output redirection and return buffer.
-        Safe to call multiple times.
-        """
-        if self.stdout0:
-            sys.stdout = self.stdout0
-            sys.stderr = self.stderr0
-            self.stdout0 = None
-            self.stderr0 = None
-        return self.outputBuffer.getvalue()
-
-
-    def stopTest(self, test):
-        # Usually one of addSuccess, addError or addFailure would have been called.
-        # But there are some path in unittest that would bypass this.
-        # We must disconnect stdout in stopTest(), which is guaranteed to be called.
-        self.complete_output()
-
-
-    def addSuccess(self, test):
-        self.success_count += 1
-        TestResult.addSuccess(self, test)
-        output = self.complete_output()
-        self.result.append((0, test, output, ''))
-        if self.verbosity > 1:
-            sys.stderr.write('ok ')
-            sys.stderr.write(str(test))
-            sys.stderr.write('\n')
-        else:
-            sys.stderr.write('.')
-
-    def addError(self, test, err):
-        self.error_count += 1
-        TestResult.addError(self, test, err)
-        _, _exc_str = self.errors[-1]
-        output = self.complete_output()
-        self.result.append((2, test, output, _exc_str))
-        if self.verbosity > 1:
-            sys.stderr.write('E  ')
-            sys.stderr.write(str(test))
-            sys.stderr.write('\n')
-        else:
-            sys.stderr.write('E')
-
-    def addFailure(self, test, err):
-        self.failure_count += 1
-        TestResult.addFailure(self, test, err)
-        _, _exc_str = self.failures[-1]
-        output = self.complete_output()
-        self.result.append((1, test, output, _exc_str))
-        if self.verbosity > 1:
-            sys.stderr.write('F  ')
-            sys.stderr.write(str(test))
-            sys.stderr.write('\n')
-        else:
-            sys.stderr.write('F')
-
-
-class HTMLTestRunner(Template_mixin):
-    """
-    """
-    def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
-        self.stream = stream
-        self.verbosity = verbosity
-        if title is None:
-            self.title = self.DEFAULT_TITLE
-        else:
-            self.title = title
-        if description is None:
-            self.description = self.DEFAULT_DESCRIPTION
-        else:
-            self.description = description
-
-        self.startTime = datetime.datetime.now()
-
-
-    def run(self, test):
-        "Run the given test case or test suite."
-        result = _TestResult(self.verbosity)
-        test(result)
-        self.stopTime = datetime.datetime.now()
-        self.generateReport(test, result)
-        print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
-        return result
-
-
-    def sortResult(self, result_list):
-        # unittest does not seems to run in any particular order.
-        # Here at least we want to group them together by class.
-        rmap = {}
-        classes = []
-        for n,t,o,e in result_list:
-            cls = t.__class__
-            if not rmap.has_key(cls):
-                rmap[cls] = []
-                classes.append(cls)
-            rmap[cls].append((n,t,o,e))
-        r = [(cls, rmap[cls]) for cls in classes]
-        return r
-
-
-    def getReportAttributes(self, result):
-        """
-        Return report attributes as a list of (name, value).
-        Override this to add custom attributes.
-        """
-        startTime = str(self.startTime)[:19]
-        duration = str(self.stopTime - self.startTime)
-        status = []
-        if result.success_count: status.append('Pass %s'    % result.success_count)
-        if result.failure_count: status.append('Failure %s' % result.failure_count)
-        if result.error_count:   status.append('Error %s'   % result.error_count  )
-        if status:
-            status = ' '.join(status)
-        else:
-            status = 'none'
-        return [
-            ('Start Time', startTime),
-            ('Duration', duration),
-            ('Status', status),
-        ]
-
-
-    def generateReport(self, test, result):
-        report_attrs = self.getReportAttributes(result)
-        generator = 'HTMLTestRunner %s' % __version__
-        stylesheet = self._generate_stylesheet()
-        heading = self._generate_heading(report_attrs)
-        report = self._generate_report(result)
-        ending = self._generate_ending()
-        output = self.HTML_TMPL % dict(
-            title = saxutils.escape(self.title),
-            generator = generator,
-            stylesheet = stylesheet,
-            heading = heading,
-            report = report,
-            ending = ending,
-        )
-        self.stream.write(output.encode('utf8'))
-
-
-    def _generate_stylesheet(self):
-        return self.STYLESHEET_TMPL
-
-
-    def _generate_heading(self, report_attrs):
-        a_lines = []
-        for name, value in report_attrs:
-            line = self.HEADING_ATTRIBUTE_TMPL % dict(
-                    name = saxutils.escape(name),
-                    value = saxutils.escape(value),
-                )
-            a_lines.append(line)
-        heading = self.HEADING_TMPL % dict(
-            title = saxutils.escape(self.title),
-            parameters = ''.join(a_lines),
-            description = saxutils.escape(self.description),
-        )
-        return heading
-
-
-    def _generate_report(self, result):
-        rows = []
-        sortedResult = self.sortResult(result.result)
-        for cid, (cls, cls_results) in enumerate(sortedResult):
-            # subtotal for a class
-            np = nf = ne = 0
-            for n,t,o,e in cls_results:
-                if n == 0: np += 1
-                elif n == 1: nf += 1
-                else: ne += 1
-
-            # format class description
-            if cls.__module__ == "__main__":
-                name = cls.__name__
-            else:
-                name = "%s.%s" % (cls.__module__, cls.__name__)
-            doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
-            desc = doc and '%s: %s' % (name, doc) or name
-
-            row = self.REPORT_CLASS_TMPL % dict(
-                style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
-                desc = desc,
-                count = np+nf+ne,
-                Pass = np,
-                fail = nf,
-                error = ne,
-                cid = 'c%s' % (cid+1),
-            )
-            rows.append(row)
-
-            for tid, (n,t,o,e) in enumerate(cls_results):
-                self._generate_report_test(rows, cid, tid, n, t, o, e)
-
-        report = self.REPORT_TMPL % dict(
-            test_list = ''.join(rows),
-            count = str(result.success_count+result.failure_count+result.error_count),
-            Pass = str(result.success_count),
-            fail = str(result.failure_count),
-            error = str(result.error_count),
-        )
-        return report
-
-
-    def _generate_report_test(self, rows, cid, tid, n, t, o, e):
-        # e.g. 'pt1.1', 'ft1.1', etc
-        has_output = bool(o or e)
-        tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
-        name = t.id().split('.')[-1]
-        doc = t.shortDescription() or ""
-        desc = doc and ('%s: %s' % (name, doc)) or name
-        tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
-
-        # o and e should be byte string because they are collected from stdout and stderr?
-        if isinstance(o,str):
-            # TODO: some problem with 'string_escape': it escape \n and mess up formating
-            # uo = unicode(o.encode('string_escape'))
-            uo = o.decode('latin-1')
-        else:
-            uo = o
-        if isinstance(e,str):
-            # TODO: some problem with 'string_escape': it escape \n and mess up formating
-            # ue = unicode(e.encode('string_escape'))
-            ue = e.decode('latin-1')
-        else:
-            ue = e
-
-        script = self.REPORT_TEST_OUTPUT_TMPL % dict(
-            id = tid,
-            output = saxutils.escape(uo+ue),
-        )
-
-        row = tmpl % dict(
-            tid = tid,
-            Class = (n == 0 and 'hiddenRow' or 'none'),
-            style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
-            desc = desc,
-            script = script,
-            status = self.STATUS[n],
-        )
-        rows.append(row)
-        if not has_output:
-            return
-
-    def _generate_ending(self):
-        return self.ENDING_TMPL
-
-
-##############################################################################
-# Facilities for running tests from the command line
-##############################################################################
-
-# Note: Reuse unittest.TestProgram to launch test. In the future we may
-# build our own launcher to support more specific command line
-# parameters like test title, CSS, etc.
-class TestProgram(unittest.TestProgram):
-    """
-    A variation of the unittest.TestProgram. Please refer to the base
-    class for command line parameters.
-    """
-    def runTests(self):
-        # Pick HTMLTestRunner as the default test runner.
-        # base class's testRunner parameter is not useful because it means
-        # we have to instantiate HTMLTestRunner before we know self.verbosity.
-        if self.testRunner is None:
-            self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
-        unittest.TestProgram.runTests(self)
-
-main = TestProgram
-
-##############################################################################
-# Executing this module from the command line
-##############################################################################
-
-if __name__ == "__main__":
-    main(module=None)
diff --git a/test/__TOOLS__/tools.py b/test/__TOOLS__/tools.py
deleted file mode 100644 (file)
index daf88a7..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import tempfile
-import sys
-import subprocess
-
-class outRedirection():
-    '''redirection of standart output
-    useful for testing the terminal display
-    '''
-    def __init__(self):
-        '''initialization
-        '''
-        self._fstream = tempfile.NamedTemporaryFile(mode='w')
-        self.saveout = sys.stdout
-        sys.stdout = self._fstream
-
-    def flush(self):
-        self._fstream.flush()                
-
-    def end_redirection(self):
-        self._fstream.seek(0)
-        ff = open(self._fstream.name, 'r')
-        self.res = ff.read()
-        self._fstream.close()
-        sys.stdout = self.saveout
-        
-    def read_results(self):
-        try:
-            return self.res
-        except Exception as exc:
-            print('Problem with redirection : %s' % exc)
-            sys.exit(1)
-            
-def check_proc_existence(cmd, text_to_find):
-    
-    p = subprocess.Popen(cmd, shell=True)
-    p.communicate()
\ No newline at end of file
diff --git a/test/_testTools/HTMLTestRunner.py b/test/_testTools/HTMLTestRunner.py
new file mode 100644 (file)
index 0000000..0439bf4
--- /dev/null
@@ -0,0 +1,824 @@
+"""
+A TestRunner for use with the Python unit testing framework. It
+generates a HTML report to show the result at a glance.
+
+The simplest way to use this is to invoke its main method. E.g.
+
+    import unittest
+    import HTMLTestRunner
+
+    ... define your tests ...
+
+    if __name__ == '__main__':
+        HTMLTestRunner.main()
+
+
+For more customization options, instantiates a HTMLTestRunner object.
+HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
+
+    # output to a file
+    fp = file('my_report.html', 'wb')
+    runner = HTMLTestRunner.HTMLTestRunner(
+                stream=fp,
+                title='My unit test',
+                description='This demonstrates the report output by HTMLTestRunner.'
+                )
+
+    # Use an external stylesheet.
+    # See the Template_mixin class for more customizable options
+    runner.STYLESHEET_TMPL = '<link rel="stylesheet" href="my_stylesheet.css" type="text/css">'
+
+    # run the test
+    runner.run(my_test_suite)
+
+
+------------------------------------------------------------------------
+Copyright (c) 2004-2007, Wai Yip Tung
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+* Neither the name Wai Yip Tung nor the names of its contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+# URL: http://tungwaiyip.info/software/HTMLTestRunner.html
+
+__author__ = "Wai Yip Tung"
+__version__ = "0.8.2"
+
+
+"""
+Change History
+
+Version 0.8.2
+* Show output inline instead of popup window (Viorel Lupu).
+
+Version in 0.8.1
+* Validated XHTML (Wolfgang Borgert).
+* Added description of test classes and test cases.
+
+Version in 0.8.0
+* Define Template_mixin class for customization.
+* Workaround a IE 6 bug that it does not treat <script> block as CDATA.
+
+Version in 0.7.1
+* Back port to Python 2.3 (Frank Horowitz).
+* Fix missing scroll bars in detail log (Podi).
+"""
+
+# TODO: color stderr
+# TODO: simplify javascript using ,ore than 1 class in the class attribute?
+
+import datetime
+import StringIO
+import sys
+import time
+import unittest
+from xml.sax import saxutils
+
+
+# ------------------------------------------------------------------------
+# The redirectors below are used to capture output during testing. Output
+# sent to sys.stdout and sys.stderr are automatically captured. However
+# in some cases sys.stdout is already cached before HTMLTestRunner is
+# invoked (e.g. calling logging.basicConfig). In order to capture those
+# output, use the redirectors for the cached stream.
+#
+# e.g.
+#   >>> logging.basicConfig(stream=HTMLTestRunner.stdout_redirector)
+#   >>>
+
+class OutputRedirector(object):
+    """ Wrapper to redirect stdout or stderr """
+    def __init__(self, fp):
+        self.fp = fp
+
+    def write(self, s):
+        self.fp.write(s)
+
+    def writelines(self, lines):
+        self.fp.writelines(lines)
+
+    def flush(self):
+        self.fp.flush()
+
+stdout_redirector = OutputRedirector(sys.stdout)
+stderr_redirector = OutputRedirector(sys.stderr)
+
+
+
+# ----------------------------------------------------------------------
+# Template
+
+class Template_mixin(object):
+    """
+    Define a HTML template for report customerization and generation.
+
+    Overall structure of an HTML report
+
+    HTML
+    +------------------------+
+    |<html>                  |
+    |  <head>                |
+    |                        |
+    |   STYLESHEET           |
+    |   +----------------+   |
+    |   |                |   |
+    |   +----------------+   |
+    |                        |
+    |  </head>               |
+    |                        |
+    |  <body>                |
+    |                        |
+    |   HEADING              |
+    |   +----------------+   |
+    |   |                |   |
+    |   +----------------+   |
+    |                        |
+    |   REPORT               |
+    |   +----------------+   |
+    |   |                |   |
+    |   +----------------+   |
+    |                        |
+    |   ENDING               |
+    |   +----------------+   |
+    |   |                |   |
+    |   +----------------+   |
+    |                        |
+    |  </body>               |
+    |</html>                 |
+    +------------------------+
+    """
+
+    STATUS = {
+    0: 'pass',
+    1: 'fail',
+    2: 'error',
+    }
+
+    DEFAULT_TITLE = 'Unit Test Report'
+    DEFAULT_DESCRIPTION = ''
+
+    # ------------------------------------------------------------------------
+    # HTML Template
+
+    HTML_TMPL = r"""<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <title>%(title)s</title>
+    <meta name="generator" content="%(generator)s"/>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+    %(stylesheet)s
+</head>
+<body>
+<script language="javascript" type="text/javascript"><!--
+output_list = Array();
+
+/* level - 0:Summary; 1:Failed; 2:All */
+function showCase(level) {
+    trs = document.getElementsByTagName("tr");
+    for (var i = 0; i < trs.length; i++) {
+        tr = trs[i];
+        id = tr.id;
+        if (id.substr(0,2) == 'ft') {
+            if (level < 1) {
+                tr.className = 'hiddenRow';
+            }
+            else {
+                tr.className = '';
+            }
+        }
+        if (id.substr(0,2) == 'pt') {
+            if (level > 1) {
+                tr.className = '';
+            }
+            else {
+                tr.className = 'hiddenRow';
+            }
+        }
+    }
+}
+
+
+function showClassDetail(cid, count) {
+    var id_list = Array(count);
+    var toHide = 1;
+    for (var i = 0; i < count; i++) {
+        tid0 = 't' + cid.substr(1) + '.' + (i+1);
+        tid = 'f' + tid0;
+        tr = document.getElementById(tid);
+        if (!tr) {
+            tid = 'p' + tid0;
+            tr = document.getElementById(tid);
+        }
+        id_list[i] = tid;
+        if (tr.className) {
+            toHide = 0;
+        }
+    }
+    for (var i = 0; i < count; i++) {
+        tid = id_list[i];
+        if (toHide) {
+            document.getElementById('div_'+tid).style.display = 'none'
+            document.getElementById(tid).className = 'hiddenRow';
+        }
+        else {
+            document.getElementById(tid).className = '';
+        }
+    }
+}
+
+
+function showTestDetail(div_id){
+    var details_div = document.getElementById(div_id)
+    var displayState = details_div.style.display
+    // alert(displayState)
+    if (displayState != 'block' ) {
+        displayState = 'block'
+        details_div.style.display = 'block'
+    }
+    else {
+        details_div.style.display = 'none'
+    }
+}
+
+
+function html_escape(s) {
+    s = s.replace(/&/g,'&amp;');
+    s = s.replace(/</g,'&lt;');
+    s = s.replace(/>/g,'&gt;');
+    return s;
+}
+
+/* obsoleted by detail in <div>
+function showOutput(id, name) {
+    var w = window.open("", //url
+                    name,
+                    "resizable,scrollbars,status,width=800,height=450");
+    d = w.document;
+    d.write("<pre>");
+    d.write(html_escape(output_list[id]));
+    d.write("\n");
+    d.write("<a href='javascript:window.close()'>close</a>\n");
+    d.write("</pre>\n");
+    d.close();
+}
+*/
+--></script>
+
+%(heading)s
+%(report)s
+%(ending)s
+
+</body>
+</html>
+"""
+    # variables: (title, generator, stylesheet, heading, report, ending)
+
+
+    # ------------------------------------------------------------------------
+    # Stylesheet
+    #
+    # alternatively use a <link> for external style sheet, e.g.
+    #   <link rel="stylesheet" href="$url" type="text/css">
+
+    STYLESHEET_TMPL = """
+<style type="text/css" media="screen">
+body        { font-family: verdana, arial, helvetica, sans-serif; font-size: 80%; }
+table       { font-size: 100%; }
+pre         { }
+
+/* -- heading ---------------------------------------------------------------------- */
+h1 {
+       font-size: 16pt;
+       color: gray;
+}
+.heading {
+    margin-top: 0ex;
+    margin-bottom: 1ex;
+}
+
+.heading .attribute {
+    margin-top: 1ex;
+    margin-bottom: 0;
+}
+
+.heading .description {
+    margin-top: 4ex;
+    margin-bottom: 6ex;
+}
+
+/* -- css div popup ------------------------------------------------------------------------ */
+a.popup_link {
+}
+
+a.popup_link:hover {
+    color: red;
+}
+
+.popup_window {
+    display: none;
+    position: relative;
+    left: 0px;
+    top: 0px;
+    /*border: solid #627173 1px; */
+    padding: 10px;
+    background-color: #E6E6D6;
+    font-family: "Lucida Console", "Courier New", Courier, monospace;
+    text-align: left;
+    font-size: 8pt;
+    width: 500px;
+}
+
+}
+/* -- report ------------------------------------------------------------------------ */
+#show_detail_line {
+    margin-top: 3ex;
+    margin-bottom: 1ex;
+}
+#result_table {
+    width: 80%;
+    border-collapse: collapse;
+    border: 1px solid #777;
+}
+#header_row {
+    font-weight: bold;
+    color: white;
+    background-color: #777;
+}
+#result_table td {
+    border: 1px solid #777;
+    padding: 2px;
+}
+#total_row  { font-weight: bold; }
+.passClass  { background-color: #6c6; }
+.failClass  { background-color: #c60; }
+.errorClass { background-color: #c00; }
+.passCase   { color: #6c6; }
+.failCase   { color: #c60; font-weight: bold; }
+.errorCase  { color: #c00; font-weight: bold; }
+.hiddenRow  { display: none; }
+.testcase   { margin-left: 2em; }
+
+
+/* -- ending ---------------------------------------------------------------------- */
+#ending {
+}
+
+</style>
+"""
+
+
+
+    # ------------------------------------------------------------------------
+    # Heading
+    #
+
+    HEADING_TMPL = """<div class='heading'>
+<h1>%(title)s</h1>
+%(parameters)s
+<p class='description'>%(description)s</p>
+</div>
+
+""" # variables: (title, parameters, description)
+
+    HEADING_ATTRIBUTE_TMPL = """<p class='attribute'><strong>%(name)s:</strong> %(value)s</p>
+""" # variables: (name, value)
+
+
+
+    # ------------------------------------------------------------------------
+    # Report
+    #
+
+    REPORT_TMPL = """
+<p id='show_detail_line'>Show
+<a href='javascript:showCase(0)'>Summary</a>
+<a href='javascript:showCase(1)'>Failed</a>
+<a href='javascript:showCase(2)'>All</a>
+</p>
+<table id='result_table'>
+<colgroup>
+<col align='left' />
+<col align='right' />
+<col align='right' />
+<col align='right' />
+<col align='right' />
+<col align='right' />
+</colgroup>
+<tr id='header_row'>
+    <td>Test Group/Test case</td>
+    <td>Count</td>
+    <td>Pass</td>
+    <td>Fail</td>
+    <td>Error</td>
+    <td>View</td>
+</tr>
+%(test_list)s
+<tr id='total_row'>
+    <td>Total</td>
+    <td>%(count)s</td>
+    <td>%(Pass)s</td>
+    <td>%(fail)s</td>
+    <td>%(error)s</td>
+    <td>&nbsp;</td>
+</tr>
+</table>
+""" # variables: (test_list, count, Pass, fail, error)
+
+    REPORT_CLASS_TMPL = r"""
+<tr class='%(style)s'>
+    <td>%(desc)s</td>
+    <td>%(count)s</td>
+    <td>%(Pass)s</td>
+    <td>%(fail)s</td>
+    <td>%(error)s</td>
+    <td><a href="javascript:showClassDetail('%(cid)s',%(count)s)">Detail</a></td>
+</tr>
+""" # variables: (style, desc, count, Pass, fail, error, cid)
+
+
+    REPORT_TEST_WITH_OUTPUT_TMPL = r"""
+<tr id='%(tid)s' class='%(Class)s'>
+    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
+    <td colspan='5' align='center'>
+
+    <!--css div popup start-->
+    <a class="popup_link" onfocus='this.blur();' href="javascript:showTestDetail('div_%(tid)s')" >
+        %(status)s</a>
+
+    <div id='div_%(tid)s' class="popup_window">
+        <div style='text-align: right; color:red;cursor:pointer'>
+        <a onfocus='this.blur();' onclick="document.getElementById('div_%(tid)s').style.display = 'none' " >
+           [x]</a>
+        </div>
+        <pre>
+        %(script)s
+        </pre>
+    </div>
+    <!--css div popup end-->
+
+    </td>
+</tr>
+""" # variables: (tid, Class, style, desc, status)
+
+
+    REPORT_TEST_NO_OUTPUT_TMPL = r"""
+<tr id='%(tid)s' class='%(Class)s'>
+    <td class='%(style)s'><div class='testcase'>%(desc)s</div></td>
+    <td colspan='5' align='center'>%(status)s</td>
+</tr>
+""" # variables: (tid, Class, style, desc, status)
+
+
+    REPORT_TEST_OUTPUT_TMPL = r"""
+%(id)s: %(output)s
+""" # variables: (id, output)
+
+
+
+    # ------------------------------------------------------------------------
+    # ENDING
+    #
+
+    ENDING_TMPL = """<div id='ending'>&nbsp;</div>"""
+
+# -------------------- The end of the Template class -------------------
+
+
+TestResult = unittest.TestResult
+
+class _TestResult(TestResult):
+    # note: _TestResult is a pure representation of results.
+    # It lacks the output and reporting ability compares to unittest._TextTestResult.
+
+    def __init__(self, verbosity=1):
+        TestResult.__init__(self)
+        self.stdout0 = None
+        self.stderr0 = None
+        self.success_count = 0
+        self.failure_count = 0
+        self.error_count = 0
+        self.verbosity = verbosity
+
+        # result is a list of result in 4 tuple
+        # (
+        #   result code (0: success; 1: fail; 2: error),
+        #   TestCase object,
+        #   Test output (byte string),
+        #   stack trace,
+        # )
+        self.result = []
+
+
+    def startTest(self, test):
+        TestResult.startTest(self, test)
+        # just one buffer for both stdout and stderr
+        self.outputBuffer = StringIO.StringIO()
+        stdout_redirector.fp = self.outputBuffer
+        stderr_redirector.fp = self.outputBuffer
+        self.stdout0 = sys.stdout
+        self.stderr0 = sys.stderr
+        sys.stdout = stdout_redirector
+        sys.stderr = stderr_redirector
+
+
+    def complete_output(self):
+        """
+        Disconnect output redirection and return buffer.
+        Safe to call multiple times.
+        """
+        if self.stdout0:
+            sys.stdout = self.stdout0
+            sys.stderr = self.stderr0
+            self.stdout0 = None
+            self.stderr0 = None
+        return self.outputBuffer.getvalue()
+
+
+    def stopTest(self, test):
+        # Usually one of addSuccess, addError or addFailure would have been called.
+        # But there are some path in unittest that would bypass this.
+        # We must disconnect stdout in stopTest(), which is guaranteed to be called.
+        self.complete_output()
+
+
+    def addSuccess(self, test):
+        self.success_count += 1
+        TestResult.addSuccess(self, test)
+        output = self.complete_output()
+        self.result.append((0, test, output, ''))
+        if self.verbosity > 1:
+            sys.stderr.write('ok ')
+            sys.stderr.write(str(test))
+            sys.stderr.write('\n')
+        else:
+            sys.stderr.write('.')
+
+    def addError(self, test, err):
+        self.error_count += 1
+        TestResult.addError(self, test, err)
+        _, _exc_str = self.errors[-1]
+        output = self.complete_output()
+        self.result.append((2, test, output, _exc_str))
+        if self.verbosity > 1:
+            sys.stderr.write('E  ')
+            sys.stderr.write(str(test))
+            sys.stderr.write('\n')
+        else:
+            sys.stderr.write('E')
+
+    def addFailure(self, test, err):
+        self.failure_count += 1
+        TestResult.addFailure(self, test, err)
+        _, _exc_str = self.failures[-1]
+        output = self.complete_output()
+        self.result.append((1, test, output, _exc_str))
+        if self.verbosity > 1:
+            sys.stderr.write('F  ')
+            sys.stderr.write(str(test))
+            sys.stderr.write('\n')
+        else:
+            sys.stderr.write('F')
+
+
+class HTMLTestRunner(Template_mixin):
+    """
+    """
+    def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None):
+        self.stream = stream
+        self.verbosity = verbosity
+        if title is None:
+            self.title = self.DEFAULT_TITLE
+        else:
+            self.title = title
+        if description is None:
+            self.description = self.DEFAULT_DESCRIPTION
+        else:
+            self.description = description
+
+        self.startTime = datetime.datetime.now()
+
+
+    def run(self, test):
+        "Run the given test case or test suite."
+        result = _TestResult(self.verbosity)
+        test(result)
+        self.stopTime = datetime.datetime.now()
+        self.generateReport(test, result)
+        print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)
+        return result
+
+
+    def sortResult(self, result_list):
+        # unittest does not seems to run in any particular order.
+        # Here at least we want to group them together by class.
+        rmap = {}
+        classes = []
+        for n,t,o,e in result_list:
+            cls = t.__class__
+            if not rmap.has_key(cls):
+                rmap[cls] = []
+                classes.append(cls)
+            rmap[cls].append((n,t,o,e))
+        r = [(cls, rmap[cls]) for cls in classes]
+        return r
+
+
+    def getReportAttributes(self, result):
+        """
+        Return report attributes as a list of (name, value).
+        Override this to add custom attributes.
+        """
+        startTime = str(self.startTime)[:19]
+        duration = str(self.stopTime - self.startTime)
+        status = []
+        if result.success_count: status.append('Pass %s'    % result.success_count)
+        if result.failure_count: status.append('Failure %s' % result.failure_count)
+        if result.error_count:   status.append('Error %s'   % result.error_count  )
+        if status:
+            status = ' '.join(status)
+        else:
+            status = 'none'
+        return [
+            ('Start Time', startTime),
+            ('Duration', duration),
+            ('Status', status),
+        ]
+
+
+    def generateReport(self, test, result):
+        report_attrs = self.getReportAttributes(result)
+        generator = 'HTMLTestRunner %s' % __version__
+        stylesheet = self._generate_stylesheet()
+        heading = self._generate_heading(report_attrs)
+        report = self._generate_report(result)
+        ending = self._generate_ending()
+        output = self.HTML_TMPL % dict(
+            title = saxutils.escape(self.title),
+            generator = generator,
+            stylesheet = stylesheet,
+            heading = heading,
+            report = report,
+            ending = ending,
+        )
+        self.stream.write(output.encode('utf8'))
+
+
+    def _generate_stylesheet(self):
+        return self.STYLESHEET_TMPL
+
+
+    def _generate_heading(self, report_attrs):
+        a_lines = []
+        for name, value in report_attrs:
+            line = self.HEADING_ATTRIBUTE_TMPL % dict(
+                    name = saxutils.escape(name),
+                    value = saxutils.escape(value),
+                )
+            a_lines.append(line)
+        heading = self.HEADING_TMPL % dict(
+            title = saxutils.escape(self.title),
+            parameters = ''.join(a_lines),
+            description = saxutils.escape(self.description),
+        )
+        return heading
+
+
+    def _generate_report(self, result):
+        rows = []
+        sortedResult = self.sortResult(result.result)
+        for cid, (cls, cls_results) in enumerate(sortedResult):
+            # subtotal for a class
+            np = nf = ne = 0
+            for n,t,o,e in cls_results:
+                if n == 0: np += 1
+                elif n == 1: nf += 1
+                else: ne += 1
+
+            # format class description
+            if cls.__module__ == "__main__":
+                name = cls.__name__
+            else:
+                name = "%s.%s" % (cls.__module__, cls.__name__)
+            doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
+            desc = doc and '%s: %s' % (name, doc) or name
+
+            row = self.REPORT_CLASS_TMPL % dict(
+                style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass',
+                desc = desc,
+                count = np+nf+ne,
+                Pass = np,
+                fail = nf,
+                error = ne,
+                cid = 'c%s' % (cid+1),
+            )
+            rows.append(row)
+
+            for tid, (n,t,o,e) in enumerate(cls_results):
+                self._generate_report_test(rows, cid, tid, n, t, o, e)
+
+        report = self.REPORT_TMPL % dict(
+            test_list = ''.join(rows),
+            count = str(result.success_count+result.failure_count+result.error_count),
+            Pass = str(result.success_count),
+            fail = str(result.failure_count),
+            error = str(result.error_count),
+        )
+        return report
+
+
+    def _generate_report_test(self, rows, cid, tid, n, t, o, e):
+        # e.g. 'pt1.1', 'ft1.1', etc
+        has_output = bool(o or e)
+        tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1)
+        name = t.id().split('.')[-1]
+        doc = t.shortDescription() or ""
+        desc = doc and ('%s: %s' % (name, doc)) or name
+        tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
+
+        # o and e should be byte string because they are collected from stdout and stderr?
+        if isinstance(o,str):
+            # TODO: some problem with 'string_escape': it escape \n and mess up formating
+            # uo = unicode(o.encode('string_escape'))
+            uo = o.decode('latin-1')
+        else:
+            uo = o
+        if isinstance(e,str):
+            # TODO: some problem with 'string_escape': it escape \n and mess up formating
+            # ue = unicode(e.encode('string_escape'))
+            ue = e.decode('latin-1')
+        else:
+            ue = e
+
+        script = self.REPORT_TEST_OUTPUT_TMPL % dict(
+            id = tid,
+            output = saxutils.escape(uo+ue),
+        )
+
+        row = tmpl % dict(
+            tid = tid,
+            Class = (n == 0 and 'hiddenRow' or 'none'),
+            style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'),
+            desc = desc,
+            script = script,
+            status = self.STATUS[n],
+        )
+        rows.append(row)
+        if not has_output:
+            return
+
+    def _generate_ending(self):
+        return self.ENDING_TMPL
+
+
+##############################################################################
+# Facilities for running tests from the command line
+##############################################################################
+
+# Note: Reuse unittest.TestProgram to launch test. In the future we may
+# build our own launcher to support more specific command line
+# parameters like test title, CSS, etc.
+class TestProgram(unittest.TestProgram):
+    """
+    A variation of the unittest.TestProgram. Please refer to the base
+    class for command line parameters.
+    """
+    def runTests(self):
+        # Pick HTMLTestRunner as the default test runner.
+        # base class's testRunner parameter is not useful because it means
+        # we have to instantiate HTMLTestRunner before we know self.verbosity.
+        if self.testRunner is None:
+            self.testRunner = HTMLTestRunner(verbosity=self.verbosity)
+        unittest.TestProgram.runTests(self)
+
+main = TestProgram
+
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
+if __name__ == "__main__":
+    main(module=None)
diff --git a/test/_testTools/tools.py b/test/_testTools/tools.py
new file mode 100644 (file)
index 0000000..daf88a7
--- /dev/null
@@ -0,0 +1,54 @@
+#!/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 tempfile
+import sys
+import subprocess
+
+class outRedirection():
+    '''redirection of standart output
+    useful for testing the terminal display
+    '''
+    def __init__(self):
+        '''initialization
+        '''
+        self._fstream = tempfile.NamedTemporaryFile(mode='w')
+        self.saveout = sys.stdout
+        sys.stdout = self._fstream
+
+    def flush(self):
+        self._fstream.flush()                
+
+    def end_redirection(self):
+        self._fstream.seek(0)
+        ff = open(self._fstream.name, 'r')
+        self.res = ff.read()
+        self._fstream.close()
+        sys.stdout = self.saveout
+        
+    def read_results(self):
+        try:
+            return self.res
+        except Exception as exc:
+            print('Problem with redirection : %s' % exc)
+            sys.exit(1)
+            
+def check_proc_existence(cmd, text_to_find):
+    
+    p = subprocess.Popen(cmd, shell=True)
+    p.communicate()
\ No newline at end of file
diff --git a/test/config/create_user_pyconf.py b/test/config/create_user_pyconf.py
new file mode 100644 (file)
index 0000000..4b5a256
--- /dev/null
@@ -0,0 +1,62 @@
+#!/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 unittest
+import os
+import sys
+import shutil
+
+# get execution path
+testdir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(testdir, '..', '..', 'src'))
+sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+
+from salomeTools import salomeTools
+import HTMLTestRunner
+
+class TestConfig(unittest.TestCase):
+    '''pyunit class : each method execute one test.
+    '''
+    
+    def test_option_value(self):
+        '''Test the display of the right value of "sat config -v VARS.hostname"
+        '''
+        res = "KO"
+        user_dir = os.path.expanduser(os.path.join('~','.salomeTools'))
+        user_dir_save = os.path.expanduser(os.path.join('~','.salomeTools_save'))
+        if os.path.exists(user_dir):
+            shutil.move(user_dir, user_dir_save)
+               
+        # The command to test
+        sat = salomeTools('')
+        sat.config('-v .')
+
+        expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'salomeTools-' + sat.cfg.INTERNAL.sat_version + ".pyconf"))
+
+        if os.path.exists(expected_file):
+            res = "OK"
+
+        shutil.rmtree(user_dir)
+        shutil.move(user_dir_save, user_dir)
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, "OK")
+
+# test launch
+if __name__ == '__main__':
+    HTMLTestRunner.main()
diff --git a/test/config/integration/create_user_pyconf.py b/test/config/integration/create_user_pyconf.py
deleted file mode 100644 (file)
index 4deb1da..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import unittest
-import os
-import sys
-import shutil
-
-# get execution path
-testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__'))
-
-from salomeTools import salomeTools
-import HTMLTestRunner
-
-class TestConfig(unittest.TestCase):
-    '''pyunit class : each method execute one test.
-    '''
-    
-    def test_option_value(self):
-        '''Test the display of the right value of "sat config -v VARS.hostname"
-        '''
-        res = "KO"
-        user_dir = os.path.expanduser(os.path.join('~','.salomeTools'))
-        user_dir_save = os.path.expanduser(os.path.join('~','.salomeTools_save'))
-        if os.path.exists(user_dir):
-            shutil.move(user_dir, user_dir_save)
-               
-        # The command to test
-        sat = salomeTools('')
-        sat.config('-v .')
-
-        expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'salomeTools-' + sat.cfg.INTERNAL.sat_version + ".pyconf"))
-
-        if os.path.exists(expected_file):
-            res = "OK"
-
-        shutil.rmtree(user_dir)
-        shutil.move(user_dir_save, user_dir)
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, "OK")
-
-# test launch
-if __name__ == '__main__':
-    HTMLTestRunner.main()
\ No newline at end of file
diff --git a/test/config/integration/option_copy.py b/test/config/integration/option_copy.py
deleted file mode 100644 (file)
index 1ad39c6..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import unittest
-import os
-import sys
-import shutil
-
-# get execution path
-testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__'))
-
-from salomeTools import salomeTools
-import HTMLTestRunner
-
-class TestConfig(unittest.TestCase):
-    '''pyunit class : each method execute one test.
-    '''
-    
-    def test_option_copy(self):
-        '''Test the copy of a pyconf"
-        '''
-        res = "KO"
-        appli_to_copy = "appli-test"
-               
-        # The command to test
-        sat = salomeTools('')
-        sat.config('appli-test -c')
-
-        expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'Application', 'LOCAL_' + appli_to_copy + '.pyconf'))
-
-        if os.path.exists(expected_file):
-            res = "OK"
-            os.remove(expected_file)
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, "OK")
-
-# test launch
-if __name__ == '__main__':
-    HTMLTestRunner.main()
\ No newline at end of file
diff --git a/test/config/integration/option_value.py b/test/config/integration/option_value.py
deleted file mode 100644 (file)
index 821c76a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import unittest
-import os
-import sys
-
-# get execution path
-testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__'))
-
-from salomeTools import salomeTools
-from tools import outRedirection
-import HTMLTestRunner
-
-class TestConfig(unittest.TestCase):
-    '''pyunit class : each method execute one test.
-    '''
-    
-    def test_option_value(self):
-        '''Test the display of the right value of "sat config -v VARS.hostname"
-        '''
-        # expected value
-        expected = '\x1b[36mhostname\x1b[0m: is221560\n'
-
-        # output redirection
-        my_out = outRedirection()
-
-        # The command to test
-        sat = salomeTools('')
-        sat.config('-v VARS.hostname')
-
-        # stop output redirection
-        my_out.end_redirection()
-
-        # get results
-        res = my_out.read_results()
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, expected)
-
-    def test_option_list(self):
-        '''Test the display of the right value of "sat config -l"
-        '''
-        # expected value
-        expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n'
-
-        # output redirection
-        my_out = outRedirection()
-
-        # The command to test
-        sat = salomeTools('')
-        sat.config('-l')
-
-        # stop output redirection
-        my_out.end_redirection()
-
-        # get results
-        res = my_out.read_results()
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, expected)
-
-# test launch
-if __name__ == '__main__':
-    HTMLTestRunner.main()
\ No newline at end of file
diff --git a/test/config/integration/option_value_2.py b/test/config/integration/option_value_2.py
deleted file mode 100644 (file)
index 5f43acd..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-#  Copyright (C) 2010-2012  CEA/DEN
-#
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
-#
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
-#
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-
-import unittest
-import os
-import sys
-
-# get execution path
-testdir = os.path.dirname(os.path.realpath(__file__))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'src'))
-sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__'))
-
-from salomeTools import salomeTools
-from tools import outRedirection
-import HTMLTestRunner
-
-class TestConfig(unittest.TestCase):
-    '''pyunit class : each method execute one test.
-    '''
-    
-    def test_option_value(self):
-        '''Test the display of the right value of "sat config -v VARS.hostname"
-        '''
-        # expected value
-        expected = '\x1b[36mhostname\x1b[0m: is221560\n'
-
-        # output redirection
-        my_out = outRedirection()
-
-        # The command to test
-        sat = salomeTools('')
-        sat.config('-v VARS.python')
-
-        # stop output redirection
-        my_out.end_redirection()
-
-        # get results
-        res = my_out.read_results()
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, expected)
-
-    def test_option_list(self):
-        '''Test the display of the right value of "sat config -l"
-        '''
-        # expected value
-        expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n'
-
-        # output redirection
-        my_out = outRedirection()
-
-        # The command to test
-        sat = salomeTools('')
-        sat.config('-l')
-
-        # stop output redirection
-        my_out.end_redirection()
-
-        # get results
-        res = my_out.read_results()
-
-        # pyunit method to compare 2 str
-        self.assertEqual(res, expected)
-
-# test launch
-if __name__ == '__main__':
-    HTMLTestRunner.main()
\ No newline at end of file
diff --git a/test/config/option_copy.py b/test/config/option_copy.py
new file mode 100644 (file)
index 0000000..cd80b87
--- /dev/null
@@ -0,0 +1,57 @@
+#!/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 unittest
+import os
+import sys
+import shutil
+
+# get execution path
+testdir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(testdir, '..', '..', 'src'))
+sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+
+from salomeTools import salomeTools
+import HTMLTestRunner
+
+class TestConfig(unittest.TestCase):
+    '''pyunit class : each method execute one test.
+    '''
+    
+    def test_option_copy(self):
+        '''Test the copy of a pyconf"
+        '''
+        res = "KO"
+        appli_to_copy = "appli-test"
+               
+        # The command to test
+        sat = salomeTools('')
+        sat.config('appli-test -c')
+
+        expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'Application', 'LOCAL_' + appli_to_copy + '.pyconf'))
+
+        if os.path.exists(expected_file):
+            res = "OK"
+            os.remove(expected_file)
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, "OK")
+
+# test launch
+if __name__ == '__main__':
+    HTMLTestRunner.main()
diff --git a/test/config/option_value.py b/test/config/option_value.py
new file mode 100644 (file)
index 0000000..2b1d903
--- /dev/null
@@ -0,0 +1,82 @@
+#!/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 unittest
+import os
+import sys
+
+# get execution path
+testdir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(testdir, '..', '..', 'src'))
+sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+
+from salomeTools import salomeTools
+from tools import outRedirection
+import HTMLTestRunner
+
+class TestConfig(unittest.TestCase):
+    '''pyunit class : each method execute one test.
+    '''
+    
+    def test_option_value(self):
+        '''Test the display of the right value of "sat config -v VARS.hostname"
+        '''
+        # expected value
+        expected = '\x1b[36mhostname\x1b[0m: is221560\n'
+
+        # output redirection
+        my_out = outRedirection()
+
+        # The command to test
+        sat = salomeTools('')
+        sat.config('-v VARS.hostname')
+
+        # stop output redirection
+        my_out.end_redirection()
+
+        # get results
+        res = my_out.read_results()
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, expected)
+
+    def test_option_list(self):
+        '''Test the display of the right value of "sat config -l"
+        '''
+        # expected value
+        expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n'
+
+        # output redirection
+        my_out = outRedirection()
+
+        # The command to test
+        sat = salomeTools('')
+        sat.config('-l')
+
+        # stop output redirection
+        my_out.end_redirection()
+
+        # get results
+        res = my_out.read_results()
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, expected)
+
+# test launch
+if __name__ == '__main__':
+    HTMLTestRunner.main()
diff --git a/test/config/option_value_2.py b/test/config/option_value_2.py
new file mode 100644 (file)
index 0000000..ddf0528
--- /dev/null
@@ -0,0 +1,82 @@
+#!/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 unittest
+import os
+import sys
+
+# get execution path
+testdir = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(testdir, '..', '..', 'src'))
+sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools'))
+
+from salomeTools import salomeTools
+from tools import outRedirection
+import HTMLTestRunner
+
+class TestConfig(unittest.TestCase):
+    '''pyunit class : each method execute one test.
+    '''
+    
+    def test_option_value(self):
+        '''Test the display of the right value of "sat config -v VARS.hostname"
+        '''
+        # expected value
+        expected = '\x1b[36mhostname\x1b[0m: is221560\n'
+
+        # output redirection
+        my_out = outRedirection()
+
+        # The command to test
+        sat = salomeTools('')
+        sat.config('-v VARS.python')
+
+        # stop output redirection
+        my_out.end_redirection()
+
+        # get results
+        res = my_out.read_results()
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, expected)
+
+    def test_option_list(self):
+        '''Test the display of the right value of "sat config -l"
+        '''
+        # expected value
+        expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n'
+
+        # output redirection
+        my_out = outRedirection()
+
+        # The command to test
+        sat = salomeTools('')
+        sat.config('-l')
+
+        # stop output redirection
+        my_out.end_redirection()
+
+        # get results
+        res = my_out.read_results()
+
+        # pyunit method to compare 2 str
+        self.assertEqual(res, expected)
+
+# test launch
+if __name__ == '__main__':
+    HTMLTestRunner.main()
index ccdd9ae174fdb4909de1f8f66ddd0fb817703d02..84f863258b62c42a6d0f2840f5189872a09417dd 100755 (executable)
@@ -16,8 +16,8 @@
 #  License along with this library; if not, write to the Free Software
 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-coverage run --source=../src/config.py    config/integration/option_value.py > test_res.html
-coverage run --source=../src/config.py -a config/integration/option_value_2.py >> test_res.html
-coverage run --source=../src/config.py -a config/integration/create_user_pyconf.py >> test_res.html
-coverage run --source=../src/config.py -a config/integration/option_copy.py >> test_res.html
+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 html