+++ /dev/null
-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
+++ /dev/null
-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
--- /dev/null
+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
--- /dev/null
+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
# 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
"results in $(BUILDDIR)/doctest/output.txt."
apidoc:
- @sphinx-apidoc -o source/commands/apidoc ../src
+ @sphinx-apidoc -o src/commands/apidoc ../src
+++ /dev/null
-******
-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).
+++ /dev/null
-# -*- 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
+++ /dev/null
-************
-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)
-
--- /dev/null
+src
+===
+
+.. toctree::
+ :maxdepth: 4
+
+ src
--- /dev/null
+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:
--- /dev/null
+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:
--- /dev/null
+******
+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).
--- /dev/null
+# -*- 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
--- /dev/null
+************
+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)
+
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
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")
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-
-
-
-# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation, and that the name of Vinay Sajip
-# not be used in advertising or publicity pertaining to distribution
-# of the software without specific, written prior permission.
-# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
-# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
-# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
-# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""
-This is a configuration module for Python.
-
-This module should work under Python versions >= 2.2, and cannot be used with
-earlier versions since it uses new-style classes.
-
-Development and testing has only been carried out (so far) on Python 2.3.4 and
-Python 2.4.2. See the test module (test_config.py) included in the
-U{distribution<http://www.red-dove.com/python_config.html|_blank>} (follow the
-download link).
-
-A simple example - with the example configuration file::
-
- messages:
- [
- {
- stream : `sys.stderr`
- message: 'Welcome'
- name: 'Harry'
- }
- {
- stream : `sys.stdout`
- message: 'Welkom'
- name: 'Ruud'
- }
- {
- stream : $messages[0].stream
- message: 'Bienvenue'
- name: Yves
- }
- ]
-
-a program to read the configuration would be::
-
- from config import Config
-
- f = file('simple.cfg')
- cfg = Config(f)
- for m in cfg.messages:
- s = '%s, %s' % (m.message, m.name)
- try:
- print >> m.stream, s
- except IOError, e:
- print e
-
-which, when run, would yield the console output::
-
- Welcome, Harry
- Welkom, Ruud
- Bienvenue, Yves
-
-See U{this tutorial<http://www.red-dove.com/python_config.html|_blank>} for more
-information.
-
-#modified for salomeTools
-@version: 0.3.7.1
-
-@author: Vinay Sajip
-
-@copyright: Copyright (C) 2004-2007 Vinay Sajip. All Rights Reserved.
-
-
-@var streamOpener: The default stream opener. This is a factory function which
-takes a string (e.g. filename) and returns a stream suitable for reading. If
-unable to open the stream, an IOError exception should be thrown.
-
-The default value of this variable is L{defaultStreamOpener}. For an example
-of how it's used, see test_config.py (search for streamOpener).
-"""
-
-__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
-__status__ = "alpha"
-__version__ = "0.3.7.1" #modified for salomeTools
-__date__ = "05 October 2007"
-
-import codecs
-import os
-import sys
-
-WORD = 'a'
-NUMBER = '9'
-STRING = '"'
-EOF = ''
-LCURLY = '{'
-RCURLY = '}'
-LBRACK = '['
-LBRACK2 = 'a['
-RBRACK = ']'
-LPAREN = '('
-LPAREN2 = '(('
-RPAREN = ')'
-DOT = '.'
-COMMA = ','
-COLON = ':'
-AT = '@'
-PLUS = '+'
-MINUS = '-'
-STAR = '*'
-SLASH = '/'
-MOD = '%'
-BACKTICK = '`'
-DOLLAR = '$'
-TRUE = 'True'
-FALSE = 'False'
-NONE = 'None'
-
-WORDCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
-
-if sys.platform == 'win32':
- NEWLINE = '\r\n'
-elif os.name == 'mac':
- NEWLINE = '\r'
-else:
- NEWLINE = '\n'
-
-try:
- has_utf32 = True
-except:
- has_utf32 = False
-
-class ConfigInputStream(object):
- """
- An input stream which can read either ANSI files with default encoding
- or Unicode files with BOMs.
-
- Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
- built-in support.
- """
- def __init__(self, stream):
- """
- Initialize an instance.
-
- @param stream: The underlying stream to be read. Should be seekable.
- @type stream: A stream (file-like object).
- """
- encoding = None
- signature = stream.read(4)
- used = -1
- if has_utf32:
- if signature == codecs.BOM_UTF32_LE:
- encoding = 'utf-32le'
- elif signature == codecs.BOM_UTF32_BE:
- encoding = 'utf-32be'
- if encoding is None:
- if signature[:3] == codecs.BOM_UTF8:
- used = 3
- encoding = 'utf-8'
- elif signature[:2] == codecs.BOM_UTF16_LE:
- used = 2
- encoding = 'utf-16le'
- elif signature[:2] == codecs.BOM_UTF16_BE:
- used = 2
- encoding = 'utf-16be'
- else:
- used = 0
- if used >= 0:
- stream.seek(used)
- if encoding:
- reader = codecs.getreader(encoding)
- stream = reader(stream)
- self.stream = stream
- self.encoding = encoding
-
- def read(self, size):
- if (size == 0) or (self.encoding is None):
- rv = self.stream.read(size)
- else:
- rv = u''
- while size > 0:
- rv += self.stream.read(1)
- size -= 1
- return rv
-
- def close(self):
- self.stream.close()
-
- def readline(self):
- if self.encoding is None:
- line = ''
- else:
- line = u''
- while True:
- c = self.stream.read(1)
- if isinstance(c, bytes):
- c = c.decode()
- if c:
- line += c
- if c == '\n':
- break
- return line
-
-class ConfigOutputStream(object):
- """
- An output stream which can write either ANSI files with default encoding
- or Unicode files with BOMs.
-
- Handles UTF-8, UTF-16LE, UTF-16BE. Could handle UTF-32 if Python had
- built-in support.
- """
-
- def __init__(self, stream, encoding=None):
- """
- Initialize an instance.
-
- @param stream: The underlying stream to be written.
- @type stream: A stream (file-like object).
- @param encoding: The desired encoding.
- @type encoding: str
- """
- if encoding is not None:
- encoding = str(encoding).lower()
- self.encoding = encoding
- if encoding == "utf-8":
- stream.write(codecs.BOM_UTF8)
- elif encoding == "utf-16be":
- stream.write(codecs.BOM_UTF16_BE)
- elif encoding == "utf-16le":
- stream.write(codecs.BOM_UTF16_LE)
- elif encoding == "utf-32be":
- stream.write(codecs.BOM_UTF32_BE)
- elif encoding == "utf-32le":
- stream.write(codecs.BOM_UTF32_LE)
-
- if encoding is not None:
- writer = codecs.getwriter(encoding)
- stream = writer(stream)
- self.stream = stream
-
- def write(self, data):
- self.stream.write(data)
-
- def flush(self):
- self.stream.flush()
-
- def close(self):
- self.stream.close()
-
-def defaultStreamOpener(name):
- """
- This function returns a read-only stream, given its name. The name passed
- in should correspond to an existing stream, otherwise an exception will be
- raised.
-
- This is the default value of L{streamOpener}; assign your own callable to
- streamOpener to return streams based on names. For example, you could use
- urllib2.urlopen().
-
- @param name: The name of a stream, most commonly a file name.
- @type name: str
- @return: A stream with the specified name.
- @rtype: A read-only stream (file-like object)
- """
- return ConfigInputStream(open(name, 'rb'))
-
-streamOpener = None
-
-__resolveOverwrite__ = True
-
-class ConfigError(Exception):
- """
- This is the base class of exceptions raised by this module.
- """
- pass
-
-class ConfigFormatError(ConfigError):
- """
- This is the base class of exceptions raised due to syntax errors in
- configurations.
- """
- pass
-
-class ConfigResolutionError(ConfigError):
- """
- This is the base class of exceptions raised due to semantic errors in
- configurations.
- """
- pass
-
-def isWord(s):
- """
- See if a passed-in value is an identifier. If the value passed in is not a
- string, False is returned. An identifier consists of alphanumerics or
- underscore characters.
-
- Examples::
-
- isWord('a word') ->False
- isWord('award') -> True
- isWord(9) -> False
- isWord('a_b_c_') ->True
-
- @note: isWord('9abc') will return True - not exactly correct, but adequate
- for the way it's used here.
-
- @param s: The name to be tested
- @type s: any
- @return: True if a word, else False
- @rtype: bool
- """
- if type(s) != type(''):
- return False
- s = s.replace('_', '')
- return s.isalnum()
-
-def makePath(prefix, suffix):
- """
- Make a path from a prefix and suffix.
-
- Examples::
-
- makePath('', 'suffix') -> 'suffix'
- makePath('prefix', 'suffix') -> 'prefix.suffix'
- makePath('prefix', '[1]') -> 'prefix[1]'
-
- @param prefix: The prefix to use. If it evaluates as false, the suffix
- is returned.
- @type prefix: str
- @param suffix: The suffix to use. It is either an identifier or an
- index in brackets.
- @type suffix: str
- @return: The path concatenation of prefix and suffix, with a
- dot if the suffix is not a bracketed index.
- @rtype: str
-
- """
- if not prefix:
- rv = suffix
- elif suffix[0] == '[':
- rv = prefix + suffix
- else:
- rv = prefix + '.' + suffix
- return rv
-
-
-class Container(object):
- """
- This internal class is the base class for mappings and sequences.
-
- @ivar path: A string which describes how to get
- to this instance from the root of the hierarchy.
-
- Example::
-
- a.list.of[1].or['more'].elements
- """
- def __init__(self, parent):
- """
- Initialize an instance.
-
- @param parent: The parent of this instance in the hierarchy.
- @type parent: A L{Container} instance.
- """
- object.__setattr__(self, 'parent', parent)
-
- def setPath(self, path):
- """
- Set the path for this instance.
- @param path: The path - a string which describes how to get
- to this instance from the root of the hierarchy.
- @type path: str
- """
- object.__setattr__(self, 'path', path)
-
- def evaluate(self, item):
- """
- Evaluate items which are instances of L{Reference} or L{Expression}.
-
- L{Reference} instances are evaluated using L{Reference.resolve},
- and L{Expression} instances are evaluated using
- L{Expression.evaluate}.
-
- @param item: The item to be evaluated.
- @type item: any
- @return: If the item is an instance of L{Reference} or L{Expression},
- the evaluated value is returned, otherwise the item is returned
- unchanged.
- """
- if isinstance(item, Reference):
- item = item.resolve(self)
- elif isinstance(item, Expression):
- item = item.evaluate(self)
- return item
-
- def writeToStream(self, stream, indent, container):
- """
- Write this instance to a stream at the specified indentation level.
-
- Should be redefined in subclasses.
-
- @param stream: The stream to write to
- @type stream: A writable stream (file-like object)
- @param indent: The indentation level
- @type indent: int
- @param container: The container of this instance
- @type container: L{Container}
- @raise NotImplementedError: If a subclass does not override this
- """
- raise NotImplementedError
-
- def writeValue(self, value, stream, indent):
- if isinstance(self, Mapping):
- indstr = ' '
- else:
- indstr = indent * ' '
- if isinstance(value, Reference) or isinstance(value, Expression):
- stream.write('%s%r%s' % (indstr, value, NEWLINE))
- else:
- if isinstance(value, str): # and not isWord(value):
- value = repr(value)
- stream.write('%s%s%s' % (indstr, value, NEWLINE))
-
-class Mapping(Container):
- """
- This internal class implements key-value mappings in configurations.
- """
-
- def __init__(self, parent=None):
- """
- Initialize an instance.
-
- @param parent: The parent of this instance in the hierarchy.
- @type parent: A L{Container} instance.
- """
- Container.__init__(self, parent)
- object.__setattr__(self, 'path', '')
- object.__setattr__(self, 'data', {})
- object.__setattr__(self, 'order', []) # to preserve ordering
- object.__setattr__(self, 'comments', {})
-
- def __delitem__(self, key):
- """
- Remove an item
- """
- data = object.__getattribute__(self, 'data')
- if key not in data:
- raise AttributeError(key)
- order = object.__getattribute__(self, 'order')
- comments = object.__getattribute__(self, 'comments')
- del data[key]
- order.remove(key)
- del comments[key]
-
- def __getitem__(self, key):
- data = object.__getattribute__(self, 'data')
- if key not in data:
- raise AttributeError(key)
- rv = data[key]
- return self.evaluate(rv)
-
- __getattr__ = __getitem__
-
- '''
- def __getattribute__(self, name):
- if name == "__dict__":
- return {}
- if name in ["__methods__", "__members__"]:
- return []
- #if name == "__class__":
- # return ''
- data = object.__getattribute__(self, "data")
- useData = data.has_key(name)
- if useData:
- rv = getattr(data, name)
- else:
- rv = object.__getattribute__(self, name)
- if rv is None:
- raise AttributeError(name)
- return rv
- '''
-
- def iteritems(self):
- for key in self.keys():
- yield(key, self[key])
- raise StopIteration
-
- def __contains__(self, item):
- order = object.__getattribute__(self, 'order')
- return item in order
-
- def addMapping(self, key, value, comment, setting=False):
- """
- Add a key-value mapping with a comment.
-
- @param key: The key for the mapping.
- @type key: str
- @param value: The value for the mapping.
- @type value: any
- @param comment: The comment for the key (can be None).
- @type comment: str
- @param setting: If True, ignore clashes. This is set
- to true when called from L{__setattr__}.
- @raise ConfigFormatError: If an existing key is seen
- again and setting is False.
- """
- data = object.__getattribute__(self, 'data')
- order = object.__getattribute__(self, 'order')
- comments = object.__getattribute__(self, 'comments')
-
- data[key] = value
- if key not in order:
- order.append(key)
- elif not setting:
- raise ConfigFormatError("repeated key: %s" % key)
- comments[key] = comment
-
- def __setattr__(self, name, value):
- self.addMapping(name, value, None, True)
-
- __setitem__ = __setattr__
-
- def keys(self):
- """
- Return the keys in a similar way to a dictionary.
- """
- return object.__getattribute__(self, 'order')
-
- def get(self, key, default=None):
- """
- Allows a dictionary-style get operation.
- """
- if key in self:
- return self[key]
- return default
-
- def __str__(self):
- return str(object.__getattribute__(self, 'data'))
-
- def __repr__(self):
- return repr(object.__getattribute__(self, 'data'))
-
- def __len__(self):
- return len(object.__getattribute__(self, 'order'))
-
- def __iter__(self):
- return self.iterkeys()
-
- def iterkeys(self):
- order = object.__getattribute__(self, 'order')
- return order.__iter__()
-
- def writeToStream(self, stream, indent, container):
- """
- Write this instance to a stream at the specified indentation level.
-
- Should be redefined in subclasses.
-
- @param stream: The stream to write to
- @type stream: A writable stream (file-like object)
- @param indent: The indentation level
- @type indent: int
- @param container: The container of this instance
- @type container: L{Container}
- """
- indstr = indent * ' '
- if len(self) == 0:
- stream.write(' { }%s' % NEWLINE)
- else:
- if isinstance(container, Mapping):
- stream.write(NEWLINE)
- stream.write('%s{%s' % (indstr, NEWLINE))
- self.__save__(stream, indent + 1)
- stream.write('%s}%s' % (indstr, NEWLINE))
-
- def __save__(self, stream, indent=0):
- """
- Save this configuration to the specified stream.
- @param stream: A stream to which the configuration is written.
- @type stream: A write-only stream (file-like object).
- @param indent: The indentation level for the output.
- @type indent: int
- """
- indstr = indent * ' '
- order = object.__getattribute__(self, 'order')
- data = object.__getattribute__(self, 'data')
- maxlen = 0 # max(map(lambda x: len(x), order))
- for key in order:
- comment = self.comments[key]
- if isWord(key):
- skey = key
- else:
- skey = repr(key)
- if comment:
- stream.write('%s#%s' % (indstr, comment))
- stream.write('%s%-*s :' % (indstr, maxlen, skey))
- value = data[key]
- if isinstance(value, Container):
- value.writeToStream(stream, indent, self)
- else:
- self.writeValue(value, stream, indent)
-
-class Config(Mapping):
- """
- This class represents a configuration, and is the only one which clients
- need to interface to, under normal circumstances.
- """
-
- class Namespace(object):
- """
- This internal class is used for implementing default namespaces.
-
- An instance acts as a namespace.
- """
- def __init__(self):
- self.sys = sys
- self.os = os
-
- def __init__(self, streamOrFile=None, parent=None):
- """
- Initializes an instance.
-
- @param streamOrFile: If specified, causes this instance to be loaded
- from the stream (by calling L{load}). If a string is provided, it is
- passed to L{streamOpener} to open a stream. Otherwise, the passed
- value is assumed to be a stream and used as is.
- @type streamOrFile: A readable stream (file-like object) or a name.
- @param parent: If specified, this becomes the parent of this instance
- in the configuration hierarchy.
- @type parent: a L{Container} instance.
- """
- Mapping.__init__(self, parent)
- object.__setattr__(self, 'reader', ConfigReader(self))
- object.__setattr__(self, 'namespaces', [Config.Namespace()])
- if streamOrFile is not None:
- if isinstance(streamOrFile, str) or isinstance(streamOrFile, bytes):
- global streamOpener
- if streamOpener is None:
- streamOpener = defaultStreamOpener
- streamOrFile = streamOpener(streamOrFile)
- load = object.__getattribute__(self, "load")
- load(streamOrFile)
-
- def load(self, stream):
- """
- Load the configuration from the specified stream. Multiple streams can
- be used to populate the same instance, as long as there are no
- clashing keys. The stream is closed.
- @param stream: A stream from which the configuration is read.
- @type stream: A read-only stream (file-like object).
- @raise ConfigError: if keys in the loaded configuration clash with
- existing keys.
- @raise ConfigFormatError: if there is a syntax error in the stream.
- """
- reader = object.__getattribute__(self, 'reader')
- reader.load(stream)
- stream.close()
-
- def addNamespace(self, ns, name=None):
- """
- Add a namespace to this configuration which can be used to evaluate
- (resolve) dotted-identifier expressions.
- @param ns: The namespace to be added.
- @type ns: A module or other namespace suitable for passing as an
- argument to vars().
- @param name: A name for the namespace, which, if specified, provides
- an additional level of indirection.
- @type name: str
- """
- namespaces = object.__getattribute__(self, 'namespaces')
- if name is None:
- namespaces.append(ns)
- else:
- setattr(namespaces[0], name, ns)
-
- def removeNamespace(self, ns, name=None):
- """
- Remove a namespace added with L{addNamespace}.
- @param ns: The namespace to be removed.
- @param name: The name which was specified when L{addNamespace} was
- called.
- @type name: str
- """
- namespaces = object.__getattribute__(self, 'namespaces')
- if name is None:
- namespaces.remove(ns)
- else:
- delattr(namespaces[0], name)
-
- def __save__(self, stream, indent=0, no_close=False):
- """
- Save this configuration to the specified stream. The stream is
- closed if this is the top-level configuration in the hierarchy.
- L{Mapping.__save__} is called to do all the work.
- @param stream: A stream to which the configuration is written.
- @type stream: A write-only stream (file-like object).
- @param indent: The indentation level for the output.
- @type indent: int
- """
- Mapping.__save__(self, stream, indent)
- if indent == 0 and not no_close:
- stream.close()
-
- def getByPath(self, path):
- """
- Obtain a value in the configuration via its path.
- @param path: The path of the required value
- @type path: str
- @return the value at the specified path.
- @rtype: any
- @raise ConfigError: If the path is invalid
- """
- s = 'self.' + path
- try:
- return eval(s)
- except Exception as e:
- raise ConfigError(str(e))
-
-class Sequence(Container):
- """
- This internal class implements a value which is a sequence of other values.
- """
- class SeqIter(object):
- """
- This internal class implements an iterator for a L{Sequence} instance.
- """
- def __init__(self, seq):
- self.seq = seq
- self.limit = len(object.__getattribute__(seq, 'data'))
- self.index = 0
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.index >= self.limit:
- raise StopIteration
- rv = self.seq[self.index]
- self.index += 1
- return rv
-
- # This method is for python3 compatibility
- def __next__(self):
- if self.index >= self.limit:
- raise StopIteration
- rv = self.seq[self.index]
- self.index += 1
- return rv
-
- def __init__(self, parent=None):
- """
- Initialize an instance.
-
- @param parent: The parent of this instance in the hierarchy.
- @type parent: A L{Container} instance.
- """
- Container.__init__(self, parent)
- object.__setattr__(self, 'data', [])
- object.__setattr__(self, 'comments', [])
-
- def append(self, item, comment):
- """
- Add an item to the sequence.
-
- @param item: The item to add.
- @type item: any
- @param comment: A comment for the item.
- @type comment: str
- """
- data = object.__getattribute__(self, 'data')
- comments = object.__getattribute__(self, 'comments')
- data.append(item)
- comments.append(comment)
-
- def __getitem__(self, index):
- data = object.__getattribute__(self, 'data')
- try:
- rv = data[index]
- except (IndexError, KeyError, TypeError):
- raise ConfigResolutionError('%r is not a valid index for %r' % (index, object.__getattribute__(self, 'path')))
- if not isinstance(rv, list):
- rv = self.evaluate(rv)
- else:
- # deal with a slice
- result = []
- for a in rv:
- result.append(self.evaluate(a))
- rv = result
- return rv
-
- def __iter__(self):
- return Sequence.SeqIter(self)
-
- def __repr__(self):
- return repr(object.__getattribute__(self, 'data'))
-
- def __str__(self):
- return str(self[:]) # using the slice evaluates the contents
-
- def __len__(self):
- return len(object.__getattribute__(self, 'data'))
-
- def writeToStream(self, stream, indent, container):
- """
- Write this instance to a stream at the specified indentation level.
-
- Should be redefined in subclasses.
-
- @param stream: The stream to write to
- @type stream: A writable stream (file-like object)
- @param indent: The indentation level
- @type indent: int
- @param container: The container of this instance
- @type container: L{Container}
- """
- indstr = indent * ' '
- if len(self) == 0:
- stream.write(' [ ]%s' % NEWLINE)
- else:
- if isinstance(container, Mapping):
- stream.write(NEWLINE)
- stream.write('%s[%s' % (indstr, NEWLINE))
- self.__save__(stream, indent + 1)
- stream.write('%s]%s' % (indstr, NEWLINE))
-
- def __save__(self, stream, indent):
- """
- Save this instance to the specified stream.
- @param stream: A stream to which the configuration is written.
- @type stream: A write-only stream (file-like object).
- @param indent: The indentation level for the output, > 0
- @type indent: int
- """
- if indent == 0:
- raise ConfigError("sequence cannot be saved as a top-level item")
- data = object.__getattribute__(self, 'data')
- comments = object.__getattribute__(self, 'comments')
- indstr = indent * ' '
- for i in xrange(0, len(data)):
- value = data[i]
- comment = comments[i]
- if comment:
- stream.write('%s#%s' % (indstr, comment))
- if isinstance(value, Container):
- value.writeToStream(stream, indent, self)
- else:
- self.writeValue(value, stream, indent)
-
-class Reference(object):
- """
- This internal class implements a value which is a reference to another value.
- """
- def __init__(self, config, type, ident):
- """
- Initialize an instance.
-
- @param config: The configuration which contains this reference.
- @type config: A L{Config} instance.
- @param type: The type of reference.
- @type type: BACKTICK or DOLLAR
- @param ident: The identifier which starts the reference.
- @type ident: str
- """
- self.config = config
- self.type = type
- self.elements = [ident]
-
- def addElement(self, type, ident):
- """
- Add an element to the reference.
-
- @param type: The type of reference.
- @type type: BACKTICK or DOLLAR
- @param ident: The identifier which continues the reference.
- @type ident: str
- """
- self.elements.append((type, ident))
-
- def findConfig(self, container):
- """
- Find the closest enclosing configuration to the specified container.
-
- @param container: The container to start from.
- @type container: L{Container}
- @return: The closest enclosing configuration, or None.
- @rtype: L{Config}
- """
- while (container is not None) and not isinstance(container, Config):
- container = object.__getattribute__(container, 'parent')
- return container
-
- def resolve(self, container):
- """
- Resolve this instance in the context of a container.
-
- @param container: The container to resolve from.
- @type container: L{Container}
- @return: The resolved value.
- @rtype: any
- @raise ConfigResolutionError: If resolution fails.
- """
- rv = None
- path = object.__getattribute__(container, 'path')
- current = container
- while current is not None:
- if self.type == BACKTICK:
- namespaces = object.__getattribute__(current, 'namespaces')
- found = False
- for ns in namespaces:
- try:
- rv = eval(str(self)[1:-1], vars(ns))
- found = True
- break
- except:
- pass
- if found:
- break
- else:
- key = self.elements[0]
- try:
- rv = current[key]
- for item in self.elements[1:]:
- key = item[1]
- rv = rv[key]
- break
- except:
- rv = None
- pass
- current = object.__getattribute__(current, 'parent')
- if current is None:
- raise ConfigResolutionError("unable to evaluate %r in the configuration %s" % (self, path))
- return rv
-
- def __str__(self):
- s = self.elements[0]
- for tt, tv in self.elements[1:]:
- if tt == DOT:
- s += '.%s' % tv
- else:
- s += '[%r]' % tv
- if self.type == BACKTICK:
- return BACKTICK + s + BACKTICK
- else:
- return DOLLAR + s
-
- def __repr__(self):
- return self.__str__()
-
-class Expression(object):
- """
- This internal class implements a value which is obtained by evaluating an expression.
- """
- def __init__(self, op, lhs, rhs):
- """
- Initialize an instance.
-
- @param op: the operation expressed in the expression.
- @type op: PLUS, MINUS, STAR, SLASH, MOD
- @param lhs: the left-hand-side operand of the expression.
- @type lhs: any Expression or primary value.
- @param rhs: the right-hand-side operand of the expression.
- @type rhs: any Expression or primary value.
- """
- self.op = op
- self.lhs = lhs
- self.rhs = rhs
-
- def __str__(self):
- return '%r %s %r' % (self.lhs, self.op, self.rhs)
-
- def __repr__(self):
- return self.__str__()
-
- def evaluate(self, container):
- """
- Evaluate this instance in the context of a container.
-
- @param container: The container to evaluate in from.
- @type container: L{Container}
- @return: The evaluated value.
- @rtype: any
- @raise ConfigResolutionError: If evaluation fails.
- @raise ZeroDivideError: If division by zero occurs.
- @raise TypeError: If the operation is invalid, e.g.
- subtracting one string from another.
- """
- lhs = self.lhs
- if isinstance(lhs, Reference):
- lhs = lhs.resolve(container)
- elif isinstance(lhs, Expression):
- lhs = lhs.evaluate(container)
- rhs = self.rhs
- if isinstance(rhs, Reference):
- rhs = rhs.resolve(container)
- elif isinstance(rhs, Expression):
- rhs = rhs.evaluate(container)
- op = self.op
- if op == PLUS:
- rv = lhs + rhs
- elif op == MINUS:
- rv = lhs - rhs
- elif op == STAR:
- rv = lhs * rhs
- elif op == SLASH:
- rv = lhs / rhs
- else:
- rv = lhs % rhs
- return rv
-
-class ConfigReader(object):
- """
- This internal class implements a parser for configurations.
- """
-
- def __init__(self, config):
- self.filename = None
- self.config = config
- self.lineno = 0
- self.colno = 0
- self.lastc = None
- self.last_token = None
- self.commentchars = '#'
- self.whitespace = ' \t\r\n'
- self.quotes = '\'"'
- self.punct = ':-+*/%,.{}[]()@`$'
- self.digits = '0123456789'
- self.wordchars = '%s' % WORDCHARS # make a copy
- self.identchars = self.wordchars + self.digits
- self.pbchars = []
- self.pbtokens = []
- self.comment = None
-
- def location(self):
- """
- Return the current location (filename, line, column) in the stream
- as a string.
-
- Used when printing error messages,
-
- @return: A string representing a location in the stream being read.
- @rtype: str
- """
- return "%s(%d,%d)" % (self.filename, self.lineno, self.colno)
-
- def getChar(self):
- """
- Get the next char from the stream. Update line and column numbers
- appropriately.
-
- @return: The next character from the stream.
- @rtype: str
- """
- if self.pbchars:
- c = self.pbchars.pop()
- if isinstance(c,bytes):
- c = c.decode()
- else:
- c = self.stream.read(1)
- if isinstance(c,bytes):
- try:
- c = c.decode()
- except:
- import pdb;pdb.set_trace()
- self.colno += 1
- if c == '\n':
- self.lineno += 1
- self.colno = 1
- return c
-
- def __repr__(self):
- return "<ConfigReader at 0x%08x>" % id(self)
-
- __str__ = __repr__
-
- def getToken(self):
- """
- Get a token from the stream. String values are returned in a form
- where you need to eval() the returned value to get the actual
- string. The return value is (token_type, token_value).
-
- Multiline string tokenizing is thanks to David Janes (BlogMatrix)
-
- @return: The next token.
- @rtype: A token tuple.
- """
- if self.pbtokens:
- return self.pbtokens.pop()
- stream = self.stream
- self.comment = None
- token = ''
- tt = EOF
- while True:
- c = self.getChar()
- if not c:
- break
- elif c == '#':
- if self.comment :
- self.comment += '#' + stream.readline()
- else :
- self.comment = stream.readline()
- self.lineno += 1
- continue
- if c in self.quotes:
- token = c
- quote = c
- tt = STRING
- escaped = False
- multiline = False
- c1 = self.getChar()
- if c1 == quote:
- c2 = self.getChar()
- if c2 == quote:
- multiline = True
- token += quote
- token += quote
- else:
- self.pbchars.append(c2)
- self.pbchars.append(c1)
- else:
- self.pbchars.append(c1)
- while True:
- c = self.getChar()
- if not c:
- break
- token += c
- if (c == quote) and not escaped:
- if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'):
- break
- if c == '\\':
- escaped = not escaped
- else:
- escaped = False
- if not c:
- raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c))
- break
- if c in self.whitespace:
- self.lastc = c
- continue
- elif c in self.punct:
- token = c
- tt = c
- if (self.lastc == ']') or (self.lastc in self.identchars):
- if c == '[':
- tt = LBRACK2
- elif c == '(':
- tt = LPAREN2
- break
- elif c in self.digits:
- token = c
- tt = NUMBER
- while True:
- c = self.getChar()
- if not c:
- break
- if c in self.digits:
- token += c
- elif (c == '.') and token.find('.') < 0:
- token += c
- else:
- if c and (c not in self.whitespace):
- self.pbchars.append(c)
- break
- break
- elif c in self.wordchars:
- token = c
- tt = WORD
- c = self.getChar()
- while c and (c in self.identchars):
- token += c
- c = self.getChar()
- if c: # and c not in self.whitespace:
- self.pbchars.append(c)
- if token == "True":
- tt = TRUE
- elif token == "False":
- tt = FALSE
- elif token == "None":
- tt = NONE
- break
- else:
- raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c))
- if token:
- self.lastc = token[-1]
- else:
- self.lastc = None
- self.last_token = tt
- return (tt, token)
-
- def load(self, stream, parent=None, suffix=None):
- """
- Load the configuration from the specified stream.
-
- @param stream: A stream from which to load the configuration.
- @type stream: A stream (file-like object).
- @param parent: The parent of the configuration (to which this reader
- belongs) in the hierarchy. Specified when the configuration is
- included in another one.
- @type parent: A L{Container} instance.
- @param suffix: The suffix of this configuration in the parent
- configuration. Should be specified whenever the parent is not None.
- @raise ConfigError: If parent is specified but suffix is not.
- @raise ConfigFormatError: If there are syntax errors in the stream.
- """
- if parent is not None:
- if suffix is None:
- raise ConfigError("internal error: load called with parent but no suffix")
- self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
- self.setStream(stream)
- self.token = self.getToken()
- self.parseMappingBody(self.config)
- if self.token[0] != EOF:
- raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1]))
-
- def setStream(self, stream):
- """
- Set the stream to the specified value, and prepare to read from it.
-
- @param stream: A stream from which to load the configuration.
- @type stream: A stream (file-like object).
- """
- self.stream = stream
- if hasattr(stream, 'name'):
- filename = stream.name
- else:
- filename = '?'
- self.filename = filename
- self.lineno = 1
- self.colno = 1
-
- def match(self, t):
- """
- Ensure that the current token type matches the specified value, and
- advance to the next token.
-
- @param t: The token type to match.
- @type t: A valid token type.
- @return: The token which was last read from the stream before this
- function is called.
- @rtype: a token tuple - see L{getToken}.
- @raise ConfigFormatError: If the token does not match what's expected.
- """
- if self.token[0] != t:
- raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1]))
- rv = self.token
- self.token = self.getToken()
- return rv
-
- def parseMappingBody(self, parent):
- """
- Parse the internals of a mapping, and add entries to the provided
- L{Mapping}.
-
- @param parent: The mapping to add entries to.
- @type parent: A L{Mapping} instance.
- """
- while self.token[0] in [WORD, STRING]:
- self.parseKeyValuePair(parent)
-
- def parseKeyValuePair(self, parent):
- """
- Parse a key-value pair, and add it to the provided L{Mapping}.
-
- @param parent: The mapping to add entries to.
- @type parent: A L{Mapping} instance.
- @raise ConfigFormatError: if a syntax error is found.
- """
- comment = self.comment
- tt, tv = self.token
- if tt == WORD:
- key = tv
- suffix = tv
- elif tt == STRING:
- key = eval(tv)
- suffix = '[%s]' % tv
- else:
- msg = "%s: expecting word or string, found %r"
- raise ConfigFormatError(msg % (self.location(), tv))
- self.token = self.getToken()
- # for now, we allow key on its own as a short form of key : True
- if self.token[0] == COLON:
- self.token = self.getToken()
- value = self.parseValue(parent, suffix)
- else:
- value = True
- try:
- parent.addMapping(key, value, comment)
- except Exception as e:
- raise ConfigFormatError("%s: %s, %r" % (self.location(), e,
- self.token[1]))
- tt = self.token[0]
- if tt not in [EOF, WORD, STRING, RCURLY, COMMA]:
- msg = "%s: expecting one of EOF, WORD, STRING, \
-RCURLY, COMMA, found %r"
- raise ConfigFormatError(msg % (self.location(), self.token[1]))
- if tt == COMMA:
- self.token = self.getToken()
-
- def parseValue(self, parent, suffix):
- """
- Parse a value.
-
- @param parent: The container to which the value will be added.
- @type parent: A L{Container} instance.
- @param suffix: The suffix for the value.
- @type suffix: str
- @return: The value
- @rtype: any
- @raise ConfigFormatError: if a syntax error is found.
- """
- tt = self.token[0]
- if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR,
- TRUE, FALSE, NONE, BACKTICK, MINUS]:
- rv = self.parseScalar()
- elif tt == LBRACK:
- rv = self.parseSequence(parent, suffix)
- elif tt in [LCURLY, AT]:
- rv = self.parseMapping(parent, suffix)
- else:
- raise ConfigFormatError("%s: unexpected input: %r" %
- (self.location(), self.token[1]))
- return rv
-
- def parseSequence(self, parent, suffix):
- """
- Parse a sequence.
-
- @param parent: The container to which the sequence will be added.
- @type parent: A L{Container} instance.
- @param suffix: The suffix for the value.
- @type suffix: str
- @return: a L{Sequence} instance representing the sequence.
- @rtype: L{Sequence}
- @raise ConfigFormatError: if a syntax error is found.
- """
- rv = Sequence(parent)
- rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix))
- self.match(LBRACK)
- comment = self.comment
- tt = self.token[0]
- while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR,
- TRUE, FALSE, NONE, BACKTICK]:
- suffix = '[%d]' % len(rv)
- value = self.parseValue(parent, suffix)
- rv.append(value, comment)
- tt = self.token[0]
- comment = self.comment
- if tt == COMMA:
- self.match(COMMA)
- tt = self.token[0]
- comment = self.comment
- continue
- self.match(RBRACK)
- return rv
-
- def parseMapping(self, parent, suffix):
- """
- Parse a mapping.
-
- @param parent: The container to which the mapping will be added.
- @type parent: A L{Container} instance.
- @param suffix: The suffix for the value.
- @type suffix: str
- @return: a L{Mapping} instance representing the mapping.
- @rtype: L{Mapping}
- @raise ConfigFormatError: if a syntax error is found.
- """
- if self.token[0] == LCURLY:
- self.match(LCURLY)
- rv = Mapping(parent)
- rv.setPath(
- makePath(object.__getattribute__(parent, 'path'), suffix))
- self.parseMappingBody(rv)
- self.match(RCURLY)
- else:
- self.match(AT)
- _, fn = self.match(STRING)
- rv = Config(eval(fn), parent)
- return rv
-
- def parseScalar(self):
- """
- Parse a scalar - a terminal value such as a string or number, or
- an L{Expression} or L{Reference}.
-
- @return: the parsed scalar
- @rtype: any scalar
- @raise ConfigFormatError: if a syntax error is found.
- """
- lhs = self.parseTerm()
- tt = self.token[0]
- while tt in [PLUS, MINUS]:
- self.match(tt)
- rhs = self.parseTerm()
- lhs = Expression(tt, lhs, rhs)
- tt = self.token[0]
- return lhs
-
- def parseTerm(self):
- """
- Parse a term in an additive expression (a + b, a - b)
-
- @return: the parsed term
- @rtype: any scalar
- @raise ConfigFormatError: if a syntax error is found.
- """
- lhs = self.parseFactor()
- tt = self.token[0]
- while tt in [STAR, SLASH, MOD]:
- self.match(tt)
- rhs = self.parseFactor()
- lhs = Expression(tt, lhs, rhs)
- tt = self.token[0]
- return lhs
-
- def parseFactor(self):
- """
- Parse a factor in an multiplicative expression (a * b, a / b, a % b)
-
- @return: the parsed factor
- @rtype: any scalar
- @raise ConfigFormatError: if a syntax error is found.
- """
- tt = self.token[0]
- if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]:
- rv = self.token[1]
- if tt != WORD:
- rv = eval(rv)
- self.match(tt)
- elif tt == LPAREN:
- self.match(LPAREN)
- rv = self.parseScalar()
- self.match(RPAREN)
- elif tt == DOLLAR:
- self.match(DOLLAR)
- rv = self.parseReference(DOLLAR)
- elif tt == BACKTICK:
- self.match(BACKTICK)
- rv = self.parseReference(BACKTICK)
- self.match(BACKTICK)
- elif tt == MINUS:
- self.match(MINUS)
- rv = -self.parseScalar()
- else:
- raise ConfigFormatError("%s: unexpected input: %r" %
- (self.location(), self.token[1]))
- return rv
-
- def parseReference(self, type):
- """
- Parse a reference.
-
- @return: the parsed reference
- @rtype: L{Reference}
- @raise ConfigFormatError: if a syntax error is found.
- """
- word = self.match(WORD)
- rv = Reference(self.config, type, word[1])
- while self.token[0] in [DOT, LBRACK2]:
- self.parseSuffix(rv)
- return rv
-
- def parseSuffix(self, ref):
- """
- Parse a reference suffix.
-
- @param ref: The reference of which this suffix is a part.
- @type ref: L{Reference}.
- @raise ConfigFormatError: if a syntax error is found.
- """
- tt = self.token[0]
- if tt == DOT:
- self.match(DOT)
- word = self.match(WORD)
- ref.addElement(DOT, word[1])
- else:
- self.match(LBRACK2)
- tt, tv = self.token
- if tt not in [NUMBER, STRING]:
- raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv))
- self.token = self.getToken()
- tv = eval(tv)
- self.match(RBRACK)
- ref.addElement(LBRACK, tv)
-
-def defaultMergeResolve(map1, map2, key):
- """
- A default resolver for merge conflicts. Returns a string
- indicating what action to take to resolve the conflict.
-
- @param map1: The map being merged into.
- @type map1: L{Mapping}.
- @param map2: The map being used as the merge operand.
- @type map2: L{Mapping}.
- @param key: The key in map2 (which also exists in map1).
- @type key: str
- @return: One of "merge", "append", "mismatch" or "overwrite"
- indicating what action should be taken. This should
- be appropriate to the objects being merged - e.g.
- there is no point returning "merge" if the two objects
- are instances of L{Sequence}.
- @rtype: str
- """
- obj1 = map1[key]
- obj2 = map2[key]
- if isinstance(obj1, Mapping) and isinstance(obj2, Mapping):
- rv = "merge"
- elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence):
- rv = "append"
- else:
- rv = "mismatch"
- return rv
-
-def overwriteMergeResolve(map1, map2, key):
- """
- An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve},
- but where a "mismatch" is detected, returns "overwrite" instead.
-
- @param map1: The map being merged into.
- @type map1: L{Mapping}.
- @param map2: The map being used as the merge operand.
- @type map2: L{Mapping}.
- @param key: The key in map2 (which also exists in map1).
- @type key: str
- """
- rv = defaultMergeResolve(map1, map2, key)
- if rv == "mismatch":
- rv = "overwrite"
- return rv
-
-class ConfigMerger(object):
- """
- This class is used for merging two configurations. If a key exists in the
- merge operand but not the merge target, then the entry is copied from the
- merge operand to the merge target. If a key exists in both configurations,
- then a resolver (a callable) is called to decide how to handle the
- conflict.
- """
-
- def __init__(self, resolver=defaultMergeResolve):
- """
- Initialise an instance.
-
- @param resolver:
- @type resolver: A callable which takes the argument list
- (map1, map2, key) where map1 is the mapping being merged into,
- map2 is the merge operand and key is the clashing key. The callable
- should return a string indicating how the conflict should be resolved.
- For possible return values, see L{defaultMergeResolve}. The default
- value preserves the old behaviour
- """
- self.resolver = resolver
-
- def merge(self, merged, mergee):
- """
- Merge two configurations. The second configuration is unchanged,
- and the first is changed to reflect the results of the merge.
-
- @param merged: The configuration to merge into.
- @type merged: L{Config}.
- @param mergee: The configuration to merge.
- @type mergee: L{Config}.
- """
- self.mergeMapping(merged, mergee)
-
- def overwriteKeys(self, map1, seq2):
- """
- Renint variables. The second mapping is unchanged,
- and the first is changed depending the keys of the second mapping.
- @param map1: The mapping to reinit keys into.
- @type map1: L{Mapping}.
- @param map2: The mapping container reinit information.
- @type map2: L{Mapping}.
- """
-
- overwrite_list = object.__getattribute__(seq2, 'data')
- for overwrite_instruction in overwrite_list:
- object.__setattr__(overwrite_instruction, 'parent', map1)
- if "__condition__" in overwrite_instruction.keys():
- overwrite_condition = overwrite_instruction["__condition__"]
- if eval(overwrite_condition, globals(), map1):
- for key in overwrite_instruction.keys():
- if key == "__condition__":
- continue
- try:
- exec( 'map1.' + key + " = " + repr(overwrite_instruction[key]))
- except:
- exec('map1.' + key + " = " + str(overwrite_instruction[key]))
- else:
- for key in overwrite_instruction.keys():
- try:
- exec('map1.' + key + " = " + repr(overwrite_instruction[key]))
- except:
- exec('map1.' + key + " = " + str(overwrite_instruction[key]))
-
- def mergeMapping(self, map1, map2):
- """
- Merge two mappings recursively. The second mapping is unchanged,
- and the first is changed to reflect the results of the merge.
-
- @param map1: The mapping to merge into.
- @type map1: L{Mapping}.
- @param map2: The mapping to merge.
- @type map2: L{Mapping}.
- """
- keys = map1.keys()
- global __resolveOverwrite__
- for key in map2.keys():
- if __resolveOverwrite__ and key == "__overwrite__":
- self.overwriteKeys(map1,map2[key])
-
- elif key not in keys:
- map1[key] = map2[key]
- if isinstance(map1[key], Container) :
- object.__setattr__(map1[key], 'parent', map1)
- else:
- obj1 = map1[key]
- obj2 = map2[key]
- decision = self.resolver(map1, map2, key)
- if decision == "merge":
- self.mergeMapping(obj1, obj2)
- elif decision == "append":
- self.mergeSequence(obj1, obj2)
- elif decision == "overwrite":
- map1[key] = obj2
- if isinstance(map1[key], Container):
- object.__setattr__(map1[key], 'parent', map1)
- elif decision == "mismatch":
- self.handleMismatch(obj1, obj2)
- else:
- msg = "unable to merge: don't know how to implement %r"
- raise ValueError(msg % decision)
-
- def mergeSequence(self, seq1, seq2):
- """
- Merge two sequences. The second sequence is unchanged,
- and the first is changed to have the elements of the second
- appended to it.
-
- @param seq1: The sequence to merge into.
- @type seq1: L{Sequence}.
- @param seq2: The sequence to merge.
- @type seq2: L{Sequence}.
- """
- data1 = object.__getattribute__(seq1, 'data')
- data2 = object.__getattribute__(seq2, 'data')
- for obj in data2:
- data1.append(obj)
- comment1 = object.__getattribute__(seq1, 'comments')
- comment2 = object.__getattribute__(seq2, 'comments')
- for obj in comment2:
- comment1.append(obj)
-
- def handleMismatch(self, obj1, obj2):
- """
- Handle a mismatch between two objects.
-
- @param obj1: The object to merge into.
- @type obj1: any
- @param obj2: The object to merge.
- @type obj2: any
- """
- raise ConfigError("unable to merge %r with %r" % (obj1, obj2))
-
-class ConfigList(list):
- """
- This class implements an ordered list of configurations and allows you
- to try getting the configuration from each entry in turn, returning
- the first successfully obtained value.
- """
-
- def getByPath(self, path):
- """
- Obtain a value from the first configuration in the list which defines
- it.
-
- @param path: The path of the value to retrieve.
- @type path: str
- @return: The value from the earliest configuration in the list which
- defines it.
- @rtype: any
- @raise ConfigError: If no configuration in the list has an entry with
- the specified path.
- """
- found = False
- rv = None
- for entry in self:
- try:
- rv = entry.getByPath(path)
- found = True
- break
- except ConfigError:
- pass
- if not found:
- raise ConfigError("unable to resolve %r" % path)
- return rv
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2013 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-'''
-In this file : all functions that do a system call, like open a browser or an editor, or call a git command
-'''
-
-import sys
-import subprocess
-
-
-def show_in_editor(editor, filePath):
- '''open filePath using editor.
-
- :param editor str: The editor to use.
- :param filePath str: The path to the file to open.
- '''
- # default editor is vi
- if editor is None or len(editor) == 0:
- editor = 'vi'
-
- if '%s' not in editor:
- editor += ' %s'
-
- try:
- # launch cmd using subprocess.Popen
- cmd = editor % filePath
- p = subprocess.Popen(cmd, shell=True)
- p.communicate()
- except:
- sys.stderr.write("Unable to edit file %s\n" % filePath)
-
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+
+
+# Copyright 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
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2013 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+'''
+In this file : all functions that do a system call, like open a browser or an editor, or call a git command
+'''
+
+import sys
+import subprocess
+
+
+def show_in_editor(editor, filePath):
+ '''open filePath using editor.
+
+ :param editor str: The editor to use.
+ :param filePath str: The path to the file to open.
+ '''
+ # default editor is vi
+ if editor is None or len(editor) == 0:
+ editor = 'vi'
+
+ if '%s' not in editor:
+ editor += ' %s'
+
+ try:
+ # launch cmd using subprocess.Popen
+ cmd = editor % filePath
+ p = subprocess.Popen(cmd, shell=True)
+ p.communicate()
+ except:
+ sys.stderr.write("Unable to edit file %s\n" % filePath)
+
\ No newline at end of file
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 ):
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)
: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]
# =======================================================================================
# 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) })
# =======================================================================================
# 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:
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) } )
# 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:
# 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
'''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"))
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('~'),
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.
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:
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
'''
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
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
# 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
(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
+++ /dev/null
-"""
-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,'&');
- s = s.replace(/</g,'<');
- s = s.replace(/>/g,'>');
- 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> </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'> </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)
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import 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
--- /dev/null
+"""
+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,'&');
+ s = s.replace(/</g,'<');
+ s = s.replace(/>/g,'>');
+ 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> </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'> </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)
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import 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
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import 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()
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import 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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import 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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import 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
+++ /dev/null
-#!/usr/bin/env python
-#-*- coding:utf-8 -*-
-# Copyright (C) 2010-2012 CEA/DEN
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-import 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
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import 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()
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import 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()
--- /dev/null
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+# Copyright (C) 2010-2012 CEA/DEN
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import 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()
# 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