From: Serge Rehbinder Date: Tue, 2 Feb 2016 14:31:18 +0000 (+0100) Subject: Remarks from sprint 1 meeting : rename some files and directories, and minor code... X-Git-Tag: sprint-02~19 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=c3f03fb1a8d8a5a20baef855572c2e999433ff13;p=tools%2Fsat.git Remarks from sprint 1 meeting : rename some files and directories, and minor code changes --- diff --git a/data/software_pyconf/softA.pyconf b/data/software_pyconf/softA.pyconf deleted file mode 100644 index 0a1f036..0000000 --- a/data/software_pyconf/softA.pyconf +++ /dev/null @@ -1,36 +0,0 @@ -SOFTWARE : -{ - softA : - { - name : "softA" - has_gui : "no" - compile_method : "cmake" # ou autotools, ou script - get_method : "git" # "archive", embedded", "native" "fixed" - cvs_info: - { - server : $SITE.prepare.default_cvs_server - module_base : $SITE.prepare.cvs_dir + $name - source : 'softA_SRC' - tag : '' - } - git_info: - { - repo : $SITE.prepare.default_git_server + $VARS.sep + $name - repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name - tag : $APPLICATION.default_version_to_download - } - archive_info: - { - archive_name : $SITE.prepare.archive_dir + $VARS.sep + $name + '.tar.gz' - } - environ : - { - "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib" - "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib" - "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}" - "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"] - } - depend : [] - opt_depend : [] - } -} \ No newline at end of file diff --git a/data/software_pyconf/softB.pyconf b/data/software_pyconf/softB.pyconf deleted file mode 100644 index 3a34586..0000000 --- a/data/software_pyconf/softB.pyconf +++ /dev/null @@ -1,36 +0,0 @@ -SOFTWARE : -{ - softB : - { - name : "softB" - has_gui : "no" - compile_method : "cmake" # ou autotools, ou script - get_method : "git" # "archive", embedded", "native" "fixed" - cvs_info: - { - server : $SITE.prepare.default_cvs_server - module_base : $SITE.prepare.cvs_dir + $name - source : 'softB_SRC' - tag : '' - } - git_info: - { - repo : $SITE.prepare.default_git_server + $VARS.sep + $name - repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name - tag : $APPLICATION.default_version_to_download - } - archive_info: - { - archive_name : $SITE.prepare.archive_dir + $VARS.sep + $name + '.tar.gz' - } - environ : - { - "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib" - "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib" - "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}" - "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"] - } - depend : ['softA'] - opt_depend : [] - } -} \ No newline at end of file diff --git a/data/softwares/softA.pyconf b/data/softwares/softA.pyconf new file mode 100644 index 0000000..0a1f036 --- /dev/null +++ b/data/softwares/softA.pyconf @@ -0,0 +1,36 @@ +SOFTWARE : +{ + softA : + { + name : "softA" + has_gui : "no" + compile_method : "cmake" # ou autotools, ou script + get_method : "git" # "archive", embedded", "native" "fixed" + cvs_info: + { + server : $SITE.prepare.default_cvs_server + module_base : $SITE.prepare.cvs_dir + $name + source : 'softA_SRC' + tag : '' + } + git_info: + { + repo : $SITE.prepare.default_git_server + $VARS.sep + $name + repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name + tag : $APPLICATION.default_version_to_download + } + archive_info: + { + archive_name : $SITE.prepare.archive_dir + $VARS.sep + $name + '.tar.gz' + } + environ : + { + "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib" + "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib" + "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}" + "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"] + } + depend : [] + opt_depend : [] + } +} \ No newline at end of file diff --git a/data/softwares/softB.pyconf b/data/softwares/softB.pyconf new file mode 100644 index 0000000..3a34586 --- /dev/null +++ b/data/softwares/softB.pyconf @@ -0,0 +1,36 @@ +SOFTWARE : +{ + softB : + { + name : "softB" + has_gui : "no" + compile_method : "cmake" # ou autotools, ou script + get_method : "git" # "archive", embedded", "native" "fixed" + cvs_info: + { + server : $SITE.prepare.default_cvs_server + module_base : $SITE.prepare.cvs_dir + $name + source : 'softB_SRC' + tag : '' + } + git_info: + { + repo : $SITE.prepare.default_git_server + $VARS.sep + $name + repo_dev : $SITE.prepare.default_git_server_dev + $VARS.sep + $name + tag : $APPLICATION.default_version_to_download + } + archive_info: + { + archive_name : $SITE.prepare.archive_dir + $VARS.sep + $name + '.tar.gz' + } + environ : + { + "_LD_LIBRARY_PATH" : "${SOFT_ROOT_DIR}" + $VARS.sep + "lib" + "_PYTHONPATH" : ["${SOFT_ROOT_DIR}" + $VARS.sep + "lib" + "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR0}" + "${SOFT_ROOT_DIR}" + $VARS.sep + "${PYTHON_LIBDIR1}"] + } + depend : ['softA'] + opt_depend : [] + } +} \ No newline at end of file diff --git a/doc/Makefile b/doc/Makefile index f79ee77..a514380 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -10,7 +10,7 @@ BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) src SILENT = &>/dev/null .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest @@ -139,4 +139,4 @@ doctest: "results in $(BUILDDIR)/doctest/output.txt." apidoc: - @sphinx-apidoc -o source/commands/apidoc ../src + @sphinx-apidoc -o src/commands/apidoc ../src diff --git a/doc/source/commands/config.rst b/doc/source/commands/config.rst deleted file mode 100644 index c1f26e5..0000000 --- a/doc/source/commands/config.rst +++ /dev/null @@ -1,46 +0,0 @@ -****** -config -****** - -Description -=========== -The **config** manages sat configuration. - -Usage -===== -* List available products: :: - - sat config --list - -* Copy a product into the user personal directory: :: - - sat config --copy [new_name] - -* Edit the user configuration file: :: - - sat config --edit - -* Edit a product: :: - - sat config --edit - -* Get the value of a parameter: :: - - sat config --value - sat config --value TOOLS.prepare.cvs_server - - sat config --value - sat config SALOME_7_7_1 --value PRODUCT.out_dir - - -Configuration -============= -* SITE.config - - * **configPath**: list of directories where to find products files. - -* USER - - * **editor**: command to use to start an editor (by default vi), - * **browser**: command to use to start a browser (by default firefox), - * **pdf_viewer**: command to use to start a pdf viewer (by default evince). diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 39d1117..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,221 +0,0 @@ -# -*- coding: utf-8 -*- -# -# salomeTools documentation build configuration file, created by -# sphinx-quickstart on Wed Sep 14 11:55:14 2011. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'salomeTools' -copyright = u'2010-2016, CEA' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '5.0' -# The full version, including alpha/beta/rc tags. -release = '5.0.0dev' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# default sphinxdoc scrolls agogo traditional nature haiku -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v 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 tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'salomeToolsdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -latex_paper_size = 'a4' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'salomeTools.tex', u'salomeTools Documentation', - u'CEA', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'salometools', u'salomeTools Documentation', - [u'CEA'], 1) -] - -# Append source folder to path in order to enable autodoc -sys.path.append(os.path.join('..')) \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 30b3450..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -************ -SAlome Tools -************ - -.. note:: - - This documentation is under construction. - -SAlomeTools (sat) is a suite of commands that can be used to perform operations on SALOME. - -For example, sat allows you to compile SALOME's modules or prerequisites, create an application, run tests, create a package... - -Quick start -=========== - -.. toctree:: - :maxdepth: 1 - - Installation of salomeTools (to be competed) - Configuration (to be competed) - Usage of salomeTools (to be competed) - -List of Commands -================ - -.. toctree:: - :maxdepth: 1 - - config - -Code documentation -================== - -.. toctree:: - :maxdepth: 1 - - SAT modules - - - -Release Notes -============= -Here are the release notes for sat: - -.. toctree:: - :maxdepth: 1 - - Release Notes 5.0.0 (to be completed) - diff --git a/doc/src/commands/apidoc/modules.rst b/doc/src/commands/apidoc/modules.rst new file mode 100644 index 0000000..e9ff8ac --- /dev/null +++ b/doc/src/commands/apidoc/modules.rst @@ -0,0 +1,7 @@ +src +=== + +.. toctree:: + :maxdepth: 4 + + src diff --git a/doc/src/commands/apidoc/src.common.rst b/doc/src/commands/apidoc/src.common.rst new file mode 100644 index 0000000..90d59ee --- /dev/null +++ b/doc/src/commands/apidoc/src.common.rst @@ -0,0 +1,54 @@ +src.common package +================== + +Submodules +---------- + +src.common.architecture module +------------------------------ + +.. automodule:: src.common.architecture + :members: + :undoc-members: + :show-inheritance: + +src.common.config_pyconf module +------------------------------- + +.. automodule:: src.common.config_pyconf + :members: + :undoc-members: + :show-inheritance: + +src.common.fileSystem module +---------------------------- + +.. automodule:: src.common.fileSystem + :members: + :undoc-members: + :show-inheritance: + +src.common.options module +------------------------- + +.. automodule:: src.common.options + :members: + :undoc-members: + :show-inheritance: + +src.common.printcolors module +----------------------------- + +.. automodule:: src.common.printcolors + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: src.common + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/src/commands/apidoc/src.rst b/doc/src/commands/apidoc/src.rst new file mode 100644 index 0000000..0c66dd5 --- /dev/null +++ b/doc/src/commands/apidoc/src.rst @@ -0,0 +1,45 @@ +src package +=========== + +Subpackages +----------- + +.. toctree:: + + src.common + +Submodules +---------- + +src.config module +----------------- + +.. automodule:: src.config + :members: + :undoc-members: + :show-inheritance: + +src.salomeTools module +---------------------- + +.. automodule:: src.salomeTools + :members: + :undoc-members: + :show-inheritance: + +src.test_command module +----------------------- + +.. automodule:: src.test_command + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: src + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/src/commands/config.rst b/doc/src/commands/config.rst new file mode 100644 index 0000000..c1f26e5 --- /dev/null +++ b/doc/src/commands/config.rst @@ -0,0 +1,46 @@ +****** +config +****** + +Description +=========== +The **config** manages sat configuration. + +Usage +===== +* List available products: :: + + sat config --list + +* Copy a product into the user personal directory: :: + + sat config --copy [new_name] + +* Edit the user configuration file: :: + + sat config --edit + +* Edit a product: :: + + sat config --edit + +* Get the value of a parameter: :: + + sat config --value + sat config --value TOOLS.prepare.cvs_server + + sat config --value + sat config SALOME_7_7_1 --value PRODUCT.out_dir + + +Configuration +============= +* SITE.config + + * **configPath**: list of directories where to find products files. + +* USER + + * **editor**: command to use to start an editor (by default vi), + * **browser**: command to use to start a browser (by default firefox), + * **pdf_viewer**: command to use to start a pdf viewer (by default evince). diff --git a/doc/src/conf.py b/doc/src/conf.py new file mode 100644 index 0000000..39d1117 --- /dev/null +++ b/doc/src/conf.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# +# salomeTools documentation build configuration file, created by +# sphinx-quickstart on Wed Sep 14 11:55:14 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'salomeTools' +copyright = u'2010-2016, CEA' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '5.0' +# The full version, including alpha/beta/rc tags. +release = '5.0.0dev' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# default sphinxdoc scrolls agogo traditional nature haiku +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v 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 tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'salomeToolsdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +latex_paper_size = 'a4' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'salomeTools.tex', u'salomeTools Documentation', + u'CEA', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'salometools', u'salomeTools Documentation', + [u'CEA'], 1) +] + +# Append source folder to path in order to enable autodoc +sys.path.append(os.path.join('..')) \ No newline at end of file diff --git a/doc/src/index.rst b/doc/src/index.rst new file mode 100644 index 0000000..30b3450 --- /dev/null +++ b/doc/src/index.rst @@ -0,0 +1,49 @@ +************ +SAlome Tools +************ + +.. note:: + + This documentation is under construction. + +SAlomeTools (sat) is a suite of commands that can be used to perform operations on SALOME. + +For example, sat allows you to compile SALOME's modules or prerequisites, create an application, run tests, create a package... + +Quick start +=========== + +.. toctree:: + :maxdepth: 1 + + Installation of salomeTools (to be competed) + Configuration (to be competed) + Usage of salomeTools (to be competed) + +List of Commands +================ + +.. toctree:: + :maxdepth: 1 + + config + +Code documentation +================== + +.. toctree:: + :maxdepth: 1 + + SAT modules + + + +Release Notes +============= +Here are the release notes for sat: + +.. toctree:: + :maxdepth: 1 + + Release Notes 5.0.0 (to be completed) + diff --git a/src/common/__init__.py b/src/common/__init__.py index bad9eb5..629fb4f 100644 --- a/src/common/__init__.py +++ b/src/common/__init__.py @@ -18,11 +18,11 @@ import os -from . import config_pyconf +from . import pyconf from . import architecture from . import printcolors from . import options -from . import fileSystem +from . import system class SatException(Exception): '''rename Exception Class @@ -40,7 +40,7 @@ def ensure_path_exists(p): def check_config_has_application( config, details = None ): '''check that the config has the key APPLICATION. Else raise an exception. - :param config class 'common.config_pyconf.Config': The config. + :param config class 'common.pyconf.Config': The config. ''' if 'APPLICATION' not in config: message = _("An APPLICATION is required. Use 'config --list' to get the list of available applications.\n") diff --git a/src/common/config_pyconf.py b/src/common/config_pyconf.py deleted file mode 100644 index 53bd184..0000000 --- a/src/common/config_pyconf.py +++ /dev/null @@ -1,1704 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- - - - -# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose and without fee is hereby granted, -# provided that the above copyright notice appear in all copies and that -# both that copyright notice and this permission notice appear in -# supporting documentation, and that the name of Vinay Sajip -# not be used in advertising or publicity pertaining to distribution -# of the software without specific, written prior permission. -# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING -# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL -# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR -# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER -# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -""" -This is a configuration module for Python. - -This module should work under Python versions >= 2.2, and cannot be used with -earlier versions since it uses new-style classes. - -Development and testing has only been carried out (so far) on Python 2.3.4 and -Python 2.4.2. See the test module (test_config.py) included in the -U{distribution} (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} 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 " -__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 "" % id(self) - - __str__ = __repr__ - - def getToken(self): - """ - Get a token from the stream. String values are returned in a form - where you need to eval() the returned value to get the actual - string. The return value is (token_type, token_value). - - Multiline string tokenizing is thanks to David Janes (BlogMatrix) - - @return: The next token. - @rtype: A token tuple. - """ - if self.pbtokens: - return self.pbtokens.pop() - stream = self.stream - self.comment = None - token = '' - tt = EOF - while True: - c = self.getChar() - if not c: - break - elif c == '#': - if self.comment : - self.comment += '#' + stream.readline() - else : - self.comment = stream.readline() - self.lineno += 1 - continue - if c in self.quotes: - token = c - quote = c - tt = STRING - escaped = False - multiline = False - c1 = self.getChar() - if c1 == quote: - c2 = self.getChar() - if c2 == quote: - multiline = True - token += quote - token += quote - else: - self.pbchars.append(c2) - self.pbchars.append(c1) - else: - self.pbchars.append(c1) - while True: - c = self.getChar() - if not c: - break - token += c - if (c == quote) and not escaped: - if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'): - break - if c == '\\': - escaped = not escaped - else: - escaped = False - if not c: - raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c)) - break - if c in self.whitespace: - self.lastc = c - continue - elif c in self.punct: - token = c - tt = c - if (self.lastc == ']') or (self.lastc in self.identchars): - if c == '[': - tt = LBRACK2 - elif c == '(': - tt = LPAREN2 - break - elif c in self.digits: - token = c - tt = NUMBER - while True: - c = self.getChar() - if not c: - break - if c in self.digits: - token += c - elif (c == '.') and token.find('.') < 0: - token += c - else: - if c and (c not in self.whitespace): - self.pbchars.append(c) - break - break - elif c in self.wordchars: - token = c - tt = WORD - c = self.getChar() - while c and (c in self.identchars): - token += c - c = self.getChar() - if c: # and c not in self.whitespace: - self.pbchars.append(c) - if token == "True": - tt = TRUE - elif token == "False": - tt = FALSE - elif token == "None": - tt = NONE - break - else: - raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c)) - if token: - self.lastc = token[-1] - else: - self.lastc = None - self.last_token = tt - return (tt, token) - - def load(self, stream, parent=None, suffix=None): - """ - Load the configuration from the specified stream. - - @param stream: A stream from which to load the configuration. - @type stream: A stream (file-like object). - @param parent: The parent of the configuration (to which this reader - belongs) in the hierarchy. Specified when the configuration is - included in another one. - @type parent: A L{Container} instance. - @param suffix: The suffix of this configuration in the parent - configuration. Should be specified whenever the parent is not None. - @raise ConfigError: If parent is specified but suffix is not. - @raise ConfigFormatError: If there are syntax errors in the stream. - """ - if parent is not None: - if suffix is None: - raise ConfigError("internal error: load called with parent but no suffix") - self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix)) - self.setStream(stream) - self.token = self.getToken() - self.parseMappingBody(self.config) - if self.token[0] != EOF: - raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1])) - - def setStream(self, stream): - """ - Set the stream to the specified value, and prepare to read from it. - - @param stream: A stream from which to load the configuration. - @type stream: A stream (file-like object). - """ - self.stream = stream - if hasattr(stream, 'name'): - filename = stream.name - else: - filename = '?' - self.filename = filename - self.lineno = 1 - self.colno = 1 - - def match(self, t): - """ - Ensure that the current token type matches the specified value, and - advance to the next token. - - @param t: The token type to match. - @type t: A valid token type. - @return: The token which was last read from the stream before this - function is called. - @rtype: a token tuple - see L{getToken}. - @raise ConfigFormatError: If the token does not match what's expected. - """ - if self.token[0] != t: - raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1])) - rv = self.token - self.token = self.getToken() - return rv - - def parseMappingBody(self, parent): - """ - Parse the internals of a mapping, and add entries to the provided - L{Mapping}. - - @param parent: The mapping to add entries to. - @type parent: A L{Mapping} instance. - """ - while self.token[0] in [WORD, STRING]: - self.parseKeyValuePair(parent) - - def parseKeyValuePair(self, parent): - """ - Parse a key-value pair, and add it to the provided L{Mapping}. - - @param parent: The mapping to add entries to. - @type parent: A L{Mapping} instance. - @raise ConfigFormatError: if a syntax error is found. - """ - comment = self.comment - tt, tv = self.token - if tt == WORD: - key = tv - suffix = tv - elif tt == STRING: - key = eval(tv) - suffix = '[%s]' % tv - else: - msg = "%s: expecting word or string, found %r" - raise ConfigFormatError(msg % (self.location(), tv)) - self.token = self.getToken() - # for now, we allow key on its own as a short form of key : True - if self.token[0] == COLON: - self.token = self.getToken() - value = self.parseValue(parent, suffix) - else: - value = True - try: - parent.addMapping(key, value, comment) - except Exception as e: - raise ConfigFormatError("%s: %s, %r" % (self.location(), e, - self.token[1])) - tt = self.token[0] - if tt not in [EOF, WORD, STRING, RCURLY, COMMA]: - msg = "%s: expecting one of EOF, WORD, STRING, \ -RCURLY, COMMA, found %r" - raise ConfigFormatError(msg % (self.location(), self.token[1])) - if tt == COMMA: - self.token = self.getToken() - - def parseValue(self, parent, suffix): - """ - Parse a value. - - @param parent: The container to which the value will be added. - @type parent: A L{Container} instance. - @param suffix: The suffix for the value. - @type suffix: str - @return: The value - @rtype: any - @raise ConfigFormatError: if a syntax error is found. - """ - tt = self.token[0] - if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR, - TRUE, FALSE, NONE, BACKTICK, MINUS]: - rv = self.parseScalar() - elif tt == LBRACK: - rv = self.parseSequence(parent, suffix) - elif tt in [LCURLY, AT]: - rv = self.parseMapping(parent, suffix) - else: - raise ConfigFormatError("%s: unexpected input: %r" % - (self.location(), self.token[1])) - return rv - - def parseSequence(self, parent, suffix): - """ - Parse a sequence. - - @param parent: The container to which the sequence will be added. - @type parent: A L{Container} instance. - @param suffix: The suffix for the value. - @type suffix: str - @return: a L{Sequence} instance representing the sequence. - @rtype: L{Sequence} - @raise ConfigFormatError: if a syntax error is found. - """ - rv = Sequence(parent) - rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix)) - self.match(LBRACK) - comment = self.comment - tt = self.token[0] - while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR, - TRUE, FALSE, NONE, BACKTICK]: - suffix = '[%d]' % len(rv) - value = self.parseValue(parent, suffix) - rv.append(value, comment) - tt = self.token[0] - comment = self.comment - if tt == COMMA: - self.match(COMMA) - tt = self.token[0] - comment = self.comment - continue - self.match(RBRACK) - return rv - - def parseMapping(self, parent, suffix): - """ - Parse a mapping. - - @param parent: The container to which the mapping will be added. - @type parent: A L{Container} instance. - @param suffix: The suffix for the value. - @type suffix: str - @return: a L{Mapping} instance representing the mapping. - @rtype: L{Mapping} - @raise ConfigFormatError: if a syntax error is found. - """ - if self.token[0] == LCURLY: - self.match(LCURLY) - rv = Mapping(parent) - rv.setPath( - makePath(object.__getattribute__(parent, 'path'), suffix)) - self.parseMappingBody(rv) - self.match(RCURLY) - else: - self.match(AT) - _, fn = self.match(STRING) - rv = Config(eval(fn), parent) - return rv - - def parseScalar(self): - """ - Parse a scalar - a terminal value such as a string or number, or - an L{Expression} or L{Reference}. - - @return: the parsed scalar - @rtype: any scalar - @raise ConfigFormatError: if a syntax error is found. - """ - lhs = self.parseTerm() - tt = self.token[0] - while tt in [PLUS, MINUS]: - self.match(tt) - rhs = self.parseTerm() - lhs = Expression(tt, lhs, rhs) - tt = self.token[0] - return lhs - - def parseTerm(self): - """ - Parse a term in an additive expression (a + b, a - b) - - @return: the parsed term - @rtype: any scalar - @raise ConfigFormatError: if a syntax error is found. - """ - lhs = self.parseFactor() - tt = self.token[0] - while tt in [STAR, SLASH, MOD]: - self.match(tt) - rhs = self.parseFactor() - lhs = Expression(tt, lhs, rhs) - tt = self.token[0] - return lhs - - def parseFactor(self): - """ - Parse a factor in an multiplicative expression (a * b, a / b, a % b) - - @return: the parsed factor - @rtype: any scalar - @raise ConfigFormatError: if a syntax error is found. - """ - tt = self.token[0] - if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]: - rv = self.token[1] - if tt != WORD: - rv = eval(rv) - self.match(tt) - elif tt == LPAREN: - self.match(LPAREN) - rv = self.parseScalar() - self.match(RPAREN) - elif tt == DOLLAR: - self.match(DOLLAR) - rv = self.parseReference(DOLLAR) - elif tt == BACKTICK: - self.match(BACKTICK) - rv = self.parseReference(BACKTICK) - self.match(BACKTICK) - elif tt == MINUS: - self.match(MINUS) - rv = -self.parseScalar() - else: - raise ConfigFormatError("%s: unexpected input: %r" % - (self.location(), self.token[1])) - return rv - - def parseReference(self, type): - """ - Parse a reference. - - @return: the parsed reference - @rtype: L{Reference} - @raise ConfigFormatError: if a syntax error is found. - """ - word = self.match(WORD) - rv = Reference(self.config, type, word[1]) - while self.token[0] in [DOT, LBRACK2]: - self.parseSuffix(rv) - return rv - - def parseSuffix(self, ref): - """ - Parse a reference suffix. - - @param ref: The reference of which this suffix is a part. - @type ref: L{Reference}. - @raise ConfigFormatError: if a syntax error is found. - """ - tt = self.token[0] - if tt == DOT: - self.match(DOT) - word = self.match(WORD) - ref.addElement(DOT, word[1]) - else: - self.match(LBRACK2) - tt, tv = self.token - if tt not in [NUMBER, STRING]: - raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv)) - self.token = self.getToken() - tv = eval(tv) - self.match(RBRACK) - ref.addElement(LBRACK, tv) - -def defaultMergeResolve(map1, map2, key): - """ - A default resolver for merge conflicts. Returns a string - indicating what action to take to resolve the conflict. - - @param map1: The map being merged into. - @type map1: L{Mapping}. - @param map2: The map being used as the merge operand. - @type map2: L{Mapping}. - @param key: The key in map2 (which also exists in map1). - @type key: str - @return: One of "merge", "append", "mismatch" or "overwrite" - indicating what action should be taken. This should - be appropriate to the objects being merged - e.g. - there is no point returning "merge" if the two objects - are instances of L{Sequence}. - @rtype: str - """ - obj1 = map1[key] - obj2 = map2[key] - if isinstance(obj1, Mapping) and isinstance(obj2, Mapping): - rv = "merge" - elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence): - rv = "append" - else: - rv = "mismatch" - return rv - -def overwriteMergeResolve(map1, map2, key): - """ - An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve}, - but where a "mismatch" is detected, returns "overwrite" instead. - - @param map1: The map being merged into. - @type map1: L{Mapping}. - @param map2: The map being used as the merge operand. - @type map2: L{Mapping}. - @param key: The key in map2 (which also exists in map1). - @type key: str - """ - rv = defaultMergeResolve(map1, map2, key) - if rv == "mismatch": - rv = "overwrite" - return rv - -class ConfigMerger(object): - """ - This class is used for merging two configurations. If a key exists in the - merge operand but not the merge target, then the entry is copied from the - merge operand to the merge target. If a key exists in both configurations, - then a resolver (a callable) is called to decide how to handle the - conflict. - """ - - def __init__(self, resolver=defaultMergeResolve): - """ - Initialise an instance. - - @param resolver: - @type resolver: A callable which takes the argument list - (map1, map2, key) where map1 is the mapping being merged into, - map2 is the merge operand and key is the clashing key. The callable - should return a string indicating how the conflict should be resolved. - For possible return values, see L{defaultMergeResolve}. The default - value preserves the old behaviour - """ - self.resolver = resolver - - def merge(self, merged, mergee): - """ - Merge two configurations. The second configuration is unchanged, - and the first is changed to reflect the results of the merge. - - @param merged: The configuration to merge into. - @type merged: L{Config}. - @param mergee: The configuration to merge. - @type mergee: L{Config}. - """ - self.mergeMapping(merged, mergee) - - def overwriteKeys(self, map1, seq2): - """ - Renint variables. The second mapping is unchanged, - and the first is changed depending the keys of the second mapping. - @param map1: The mapping to reinit keys into. - @type map1: L{Mapping}. - @param map2: The mapping container reinit information. - @type map2: L{Mapping}. - """ - - overwrite_list = object.__getattribute__(seq2, 'data') - for overwrite_instruction in overwrite_list: - object.__setattr__(overwrite_instruction, 'parent', map1) - if "__condition__" in overwrite_instruction.keys(): - overwrite_condition = overwrite_instruction["__condition__"] - if eval(overwrite_condition, globals(), map1): - for key in overwrite_instruction.keys(): - if key == "__condition__": - continue - try: - exec( 'map1.' + key + " = " + repr(overwrite_instruction[key])) - except: - exec('map1.' + key + " = " + str(overwrite_instruction[key])) - else: - for key in overwrite_instruction.keys(): - try: - exec('map1.' + key + " = " + repr(overwrite_instruction[key])) - except: - exec('map1.' + key + " = " + str(overwrite_instruction[key])) - - def mergeMapping(self, map1, map2): - """ - Merge two mappings recursively. The second mapping is unchanged, - and the first is changed to reflect the results of the merge. - - @param map1: The mapping to merge into. - @type map1: L{Mapping}. - @param map2: The mapping to merge. - @type map2: L{Mapping}. - """ - keys = map1.keys() - global __resolveOverwrite__ - for key in map2.keys(): - if __resolveOverwrite__ and key == "__overwrite__": - self.overwriteKeys(map1,map2[key]) - - elif key not in keys: - map1[key] = map2[key] - if isinstance(map1[key], Container) : - object.__setattr__(map1[key], 'parent', map1) - else: - obj1 = map1[key] - obj2 = map2[key] - decision = self.resolver(map1, map2, key) - if decision == "merge": - self.mergeMapping(obj1, obj2) - elif decision == "append": - self.mergeSequence(obj1, obj2) - elif decision == "overwrite": - map1[key] = obj2 - if isinstance(map1[key], Container): - object.__setattr__(map1[key], 'parent', map1) - elif decision == "mismatch": - self.handleMismatch(obj1, obj2) - else: - msg = "unable to merge: don't know how to implement %r" - raise ValueError(msg % decision) - - def mergeSequence(self, seq1, seq2): - """ - Merge two sequences. The second sequence is unchanged, - and the first is changed to have the elements of the second - appended to it. - - @param seq1: The sequence to merge into. - @type seq1: L{Sequence}. - @param seq2: The sequence to merge. - @type seq2: L{Sequence}. - """ - data1 = object.__getattribute__(seq1, 'data') - data2 = object.__getattribute__(seq2, 'data') - for obj in data2: - data1.append(obj) - comment1 = object.__getattribute__(seq1, 'comments') - comment2 = object.__getattribute__(seq2, 'comments') - for obj in comment2: - comment1.append(obj) - - def handleMismatch(self, obj1, obj2): - """ - Handle a mismatch between two objects. - - @param obj1: The object to merge into. - @type obj1: any - @param obj2: The object to merge. - @type obj2: any - """ - raise ConfigError("unable to merge %r with %r" % (obj1, obj2)) - -class ConfigList(list): - """ - This class implements an ordered list of configurations and allows you - to try getting the configuration from each entry in turn, returning - the first successfully obtained value. - """ - - def getByPath(self, path): - """ - Obtain a value from the first configuration in the list which defines - it. - - @param path: The path of the value to retrieve. - @type path: str - @return: The value from the earliest configuration in the list which - defines it. - @rtype: any - @raise ConfigError: If no configuration in the list has an entry with - the specified path. - """ - found = False - rv = None - for entry in self: - try: - rv = entry.getByPath(path) - found = True - break - except ConfigError: - pass - if not found: - raise ConfigError("unable to resolve %r" % path) - return rv diff --git a/src/common/fileSystem.py b/src/common/fileSystem.py deleted file mode 100644 index cc8a39f..0000000 --- a/src/common/fileSystem.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2013 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -''' -In this file : all functions that do a system call, like open a browser or an editor, or call a git command -''' - -import sys -import subprocess - - -def show_in_editor(editor, filePath): - '''open filePath using editor. - - :param editor str: The editor to use. - :param filePath str: The path to the file to open. - ''' - # default editor is vi - if editor is None or len(editor) == 0: - editor = 'vi' - - if '%s' not in editor: - editor += ' %s' - - try: - # launch cmd using subprocess.Popen - cmd = editor % filePath - p = subprocess.Popen(cmd, shell=True) - p.communicate() - except: - sys.stderr.write("Unable to edit file %s\n" % filePath) - \ No newline at end of file diff --git a/src/common/pyconf.py b/src/common/pyconf.py new file mode 100644 index 0000000..53bd184 --- /dev/null +++ b/src/common/pyconf.py @@ -0,0 +1,1704 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + + + +# Copyright 2004-2007 by Vinay Sajip. All Rights Reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose and without fee is hereby granted, +# provided that the above copyright notice appear in all copies and that +# both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of Vinay Sajip +# not be used in advertising or publicity pertaining to distribution +# of the software without specific, written prior permission. +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" +This is a configuration module for Python. + +This module should work under Python versions >= 2.2, and cannot be used with +earlier versions since it uses new-style classes. + +Development and testing has only been carried out (so far) on Python 2.3.4 and +Python 2.4.2. See the test module (test_config.py) included in the +U{distribution} (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} 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 " +__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 "" % id(self) + + __str__ = __repr__ + + def getToken(self): + """ + Get a token from the stream. String values are returned in a form + where you need to eval() the returned value to get the actual + string. The return value is (token_type, token_value). + + Multiline string tokenizing is thanks to David Janes (BlogMatrix) + + @return: The next token. + @rtype: A token tuple. + """ + if self.pbtokens: + return self.pbtokens.pop() + stream = self.stream + self.comment = None + token = '' + tt = EOF + while True: + c = self.getChar() + if not c: + break + elif c == '#': + if self.comment : + self.comment += '#' + stream.readline() + else : + self.comment = stream.readline() + self.lineno += 1 + continue + if c in self.quotes: + token = c + quote = c + tt = STRING + escaped = False + multiline = False + c1 = self.getChar() + if c1 == quote: + c2 = self.getChar() + if c2 == quote: + multiline = True + token += quote + token += quote + else: + self.pbchars.append(c2) + self.pbchars.append(c1) + else: + self.pbchars.append(c1) + while True: + c = self.getChar() + if not c: + break + token += c + if (c == quote) and not escaped: + if not multiline or (len(token) >= 6 and token.endswith(token[:3]) and token[-4] != '\\'): + break + if c == '\\': + escaped = not escaped + else: + escaped = False + if not c: + raise ConfigFormatError('%s: Unterminated quoted string: %r, %r' % (self.location(), token, c)) + break + if c in self.whitespace: + self.lastc = c + continue + elif c in self.punct: + token = c + tt = c + if (self.lastc == ']') or (self.lastc in self.identchars): + if c == '[': + tt = LBRACK2 + elif c == '(': + tt = LPAREN2 + break + elif c in self.digits: + token = c + tt = NUMBER + while True: + c = self.getChar() + if not c: + break + if c in self.digits: + token += c + elif (c == '.') and token.find('.') < 0: + token += c + else: + if c and (c not in self.whitespace): + self.pbchars.append(c) + break + break + elif c in self.wordchars: + token = c + tt = WORD + c = self.getChar() + while c and (c in self.identchars): + token += c + c = self.getChar() + if c: # and c not in self.whitespace: + self.pbchars.append(c) + if token == "True": + tt = TRUE + elif token == "False": + tt = FALSE + elif token == "None": + tt = NONE + break + else: + raise ConfigFormatError('%s: Unexpected character: %r' % (self.location(), c)) + if token: + self.lastc = token[-1] + else: + self.lastc = None + self.last_token = tt + return (tt, token) + + def load(self, stream, parent=None, suffix=None): + """ + Load the configuration from the specified stream. + + @param stream: A stream from which to load the configuration. + @type stream: A stream (file-like object). + @param parent: The parent of the configuration (to which this reader + belongs) in the hierarchy. Specified when the configuration is + included in another one. + @type parent: A L{Container} instance. + @param suffix: The suffix of this configuration in the parent + configuration. Should be specified whenever the parent is not None. + @raise ConfigError: If parent is specified but suffix is not. + @raise ConfigFormatError: If there are syntax errors in the stream. + """ + if parent is not None: + if suffix is None: + raise ConfigError("internal error: load called with parent but no suffix") + self.config.setPath(makePath(object.__getattribute__(parent, 'path'), suffix)) + self.setStream(stream) + self.token = self.getToken() + self.parseMappingBody(self.config) + if self.token[0] != EOF: + raise ConfigFormatError('%s: expecting EOF, found %r' % (self.location(), self.token[1])) + + def setStream(self, stream): + """ + Set the stream to the specified value, and prepare to read from it. + + @param stream: A stream from which to load the configuration. + @type stream: A stream (file-like object). + """ + self.stream = stream + if hasattr(stream, 'name'): + filename = stream.name + else: + filename = '?' + self.filename = filename + self.lineno = 1 + self.colno = 1 + + def match(self, t): + """ + Ensure that the current token type matches the specified value, and + advance to the next token. + + @param t: The token type to match. + @type t: A valid token type. + @return: The token which was last read from the stream before this + function is called. + @rtype: a token tuple - see L{getToken}. + @raise ConfigFormatError: If the token does not match what's expected. + """ + if self.token[0] != t: + raise ConfigFormatError("%s: expecting %s, found %r" % (self.location(), t, self.token[1])) + rv = self.token + self.token = self.getToken() + return rv + + def parseMappingBody(self, parent): + """ + Parse the internals of a mapping, and add entries to the provided + L{Mapping}. + + @param parent: The mapping to add entries to. + @type parent: A L{Mapping} instance. + """ + while self.token[0] in [WORD, STRING]: + self.parseKeyValuePair(parent) + + def parseKeyValuePair(self, parent): + """ + Parse a key-value pair, and add it to the provided L{Mapping}. + + @param parent: The mapping to add entries to. + @type parent: A L{Mapping} instance. + @raise ConfigFormatError: if a syntax error is found. + """ + comment = self.comment + tt, tv = self.token + if tt == WORD: + key = tv + suffix = tv + elif tt == STRING: + key = eval(tv) + suffix = '[%s]' % tv + else: + msg = "%s: expecting word or string, found %r" + raise ConfigFormatError(msg % (self.location(), tv)) + self.token = self.getToken() + # for now, we allow key on its own as a short form of key : True + if self.token[0] == COLON: + self.token = self.getToken() + value = self.parseValue(parent, suffix) + else: + value = True + try: + parent.addMapping(key, value, comment) + except Exception as e: + raise ConfigFormatError("%s: %s, %r" % (self.location(), e, + self.token[1])) + tt = self.token[0] + if tt not in [EOF, WORD, STRING, RCURLY, COMMA]: + msg = "%s: expecting one of EOF, WORD, STRING, \ +RCURLY, COMMA, found %r" + raise ConfigFormatError(msg % (self.location(), self.token[1])) + if tt == COMMA: + self.token = self.getToken() + + def parseValue(self, parent, suffix): + """ + Parse a value. + + @param parent: The container to which the value will be added. + @type parent: A L{Container} instance. + @param suffix: The suffix for the value. + @type suffix: str + @return: The value + @rtype: any + @raise ConfigFormatError: if a syntax error is found. + """ + tt = self.token[0] + if tt in [STRING, WORD, NUMBER, LPAREN, DOLLAR, + TRUE, FALSE, NONE, BACKTICK, MINUS]: + rv = self.parseScalar() + elif tt == LBRACK: + rv = self.parseSequence(parent, suffix) + elif tt in [LCURLY, AT]: + rv = self.parseMapping(parent, suffix) + else: + raise ConfigFormatError("%s: unexpected input: %r" % + (self.location(), self.token[1])) + return rv + + def parseSequence(self, parent, suffix): + """ + Parse a sequence. + + @param parent: The container to which the sequence will be added. + @type parent: A L{Container} instance. + @param suffix: The suffix for the value. + @type suffix: str + @return: a L{Sequence} instance representing the sequence. + @rtype: L{Sequence} + @raise ConfigFormatError: if a syntax error is found. + """ + rv = Sequence(parent) + rv.setPath(makePath(object.__getattribute__(parent, 'path'), suffix)) + self.match(LBRACK) + comment = self.comment + tt = self.token[0] + while tt in [STRING, WORD, NUMBER, LCURLY, LBRACK, LPAREN, DOLLAR, + TRUE, FALSE, NONE, BACKTICK]: + suffix = '[%d]' % len(rv) + value = self.parseValue(parent, suffix) + rv.append(value, comment) + tt = self.token[0] + comment = self.comment + if tt == COMMA: + self.match(COMMA) + tt = self.token[0] + comment = self.comment + continue + self.match(RBRACK) + return rv + + def parseMapping(self, parent, suffix): + """ + Parse a mapping. + + @param parent: The container to which the mapping will be added. + @type parent: A L{Container} instance. + @param suffix: The suffix for the value. + @type suffix: str + @return: a L{Mapping} instance representing the mapping. + @rtype: L{Mapping} + @raise ConfigFormatError: if a syntax error is found. + """ + if self.token[0] == LCURLY: + self.match(LCURLY) + rv = Mapping(parent) + rv.setPath( + makePath(object.__getattribute__(parent, 'path'), suffix)) + self.parseMappingBody(rv) + self.match(RCURLY) + else: + self.match(AT) + _, fn = self.match(STRING) + rv = Config(eval(fn), parent) + return rv + + def parseScalar(self): + """ + Parse a scalar - a terminal value such as a string or number, or + an L{Expression} or L{Reference}. + + @return: the parsed scalar + @rtype: any scalar + @raise ConfigFormatError: if a syntax error is found. + """ + lhs = self.parseTerm() + tt = self.token[0] + while tt in [PLUS, MINUS]: + self.match(tt) + rhs = self.parseTerm() + lhs = Expression(tt, lhs, rhs) + tt = self.token[0] + return lhs + + def parseTerm(self): + """ + Parse a term in an additive expression (a + b, a - b) + + @return: the parsed term + @rtype: any scalar + @raise ConfigFormatError: if a syntax error is found. + """ + lhs = self.parseFactor() + tt = self.token[0] + while tt in [STAR, SLASH, MOD]: + self.match(tt) + rhs = self.parseFactor() + lhs = Expression(tt, lhs, rhs) + tt = self.token[0] + return lhs + + def parseFactor(self): + """ + Parse a factor in an multiplicative expression (a * b, a / b, a % b) + + @return: the parsed factor + @rtype: any scalar + @raise ConfigFormatError: if a syntax error is found. + """ + tt = self.token[0] + if tt in [NUMBER, WORD, STRING, TRUE, FALSE, NONE]: + rv = self.token[1] + if tt != WORD: + rv = eval(rv) + self.match(tt) + elif tt == LPAREN: + self.match(LPAREN) + rv = self.parseScalar() + self.match(RPAREN) + elif tt == DOLLAR: + self.match(DOLLAR) + rv = self.parseReference(DOLLAR) + elif tt == BACKTICK: + self.match(BACKTICK) + rv = self.parseReference(BACKTICK) + self.match(BACKTICK) + elif tt == MINUS: + self.match(MINUS) + rv = -self.parseScalar() + else: + raise ConfigFormatError("%s: unexpected input: %r" % + (self.location(), self.token[1])) + return rv + + def parseReference(self, type): + """ + Parse a reference. + + @return: the parsed reference + @rtype: L{Reference} + @raise ConfigFormatError: if a syntax error is found. + """ + word = self.match(WORD) + rv = Reference(self.config, type, word[1]) + while self.token[0] in [DOT, LBRACK2]: + self.parseSuffix(rv) + return rv + + def parseSuffix(self, ref): + """ + Parse a reference suffix. + + @param ref: The reference of which this suffix is a part. + @type ref: L{Reference}. + @raise ConfigFormatError: if a syntax error is found. + """ + tt = self.token[0] + if tt == DOT: + self.match(DOT) + word = self.match(WORD) + ref.addElement(DOT, word[1]) + else: + self.match(LBRACK2) + tt, tv = self.token + if tt not in [NUMBER, STRING]: + raise ConfigFormatError("%s: expected number or string, found %r" % (self.location(), tv)) + self.token = self.getToken() + tv = eval(tv) + self.match(RBRACK) + ref.addElement(LBRACK, tv) + +def defaultMergeResolve(map1, map2, key): + """ + A default resolver for merge conflicts. Returns a string + indicating what action to take to resolve the conflict. + + @param map1: The map being merged into. + @type map1: L{Mapping}. + @param map2: The map being used as the merge operand. + @type map2: L{Mapping}. + @param key: The key in map2 (which also exists in map1). + @type key: str + @return: One of "merge", "append", "mismatch" or "overwrite" + indicating what action should be taken. This should + be appropriate to the objects being merged - e.g. + there is no point returning "merge" if the two objects + are instances of L{Sequence}. + @rtype: str + """ + obj1 = map1[key] + obj2 = map2[key] + if isinstance(obj1, Mapping) and isinstance(obj2, Mapping): + rv = "merge" + elif isinstance(obj1, Sequence) and isinstance(obj2, Sequence): + rv = "append" + else: + rv = "mismatch" + return rv + +def overwriteMergeResolve(map1, map2, key): + """ + An overwriting resolver for merge conflicts. Calls L{defaultMergeResolve}, + but where a "mismatch" is detected, returns "overwrite" instead. + + @param map1: The map being merged into. + @type map1: L{Mapping}. + @param map2: The map being used as the merge operand. + @type map2: L{Mapping}. + @param key: The key in map2 (which also exists in map1). + @type key: str + """ + rv = defaultMergeResolve(map1, map2, key) + if rv == "mismatch": + rv = "overwrite" + return rv + +class ConfigMerger(object): + """ + This class is used for merging two configurations. If a key exists in the + merge operand but not the merge target, then the entry is copied from the + merge operand to the merge target. If a key exists in both configurations, + then a resolver (a callable) is called to decide how to handle the + conflict. + """ + + def __init__(self, resolver=defaultMergeResolve): + """ + Initialise an instance. + + @param resolver: + @type resolver: A callable which takes the argument list + (map1, map2, key) where map1 is the mapping being merged into, + map2 is the merge operand and key is the clashing key. The callable + should return a string indicating how the conflict should be resolved. + For possible return values, see L{defaultMergeResolve}. The default + value preserves the old behaviour + """ + self.resolver = resolver + + def merge(self, merged, mergee): + """ + Merge two configurations. The second configuration is unchanged, + and the first is changed to reflect the results of the merge. + + @param merged: The configuration to merge into. + @type merged: L{Config}. + @param mergee: The configuration to merge. + @type mergee: L{Config}. + """ + self.mergeMapping(merged, mergee) + + def overwriteKeys(self, map1, seq2): + """ + Renint variables. The second mapping is unchanged, + and the first is changed depending the keys of the second mapping. + @param map1: The mapping to reinit keys into. + @type map1: L{Mapping}. + @param map2: The mapping container reinit information. + @type map2: L{Mapping}. + """ + + overwrite_list = object.__getattribute__(seq2, 'data') + for overwrite_instruction in overwrite_list: + object.__setattr__(overwrite_instruction, 'parent', map1) + if "__condition__" in overwrite_instruction.keys(): + overwrite_condition = overwrite_instruction["__condition__"] + if eval(overwrite_condition, globals(), map1): + for key in overwrite_instruction.keys(): + if key == "__condition__": + continue + try: + exec( 'map1.' + key + " = " + repr(overwrite_instruction[key])) + except: + exec('map1.' + key + " = " + str(overwrite_instruction[key])) + else: + for key in overwrite_instruction.keys(): + try: + exec('map1.' + key + " = " + repr(overwrite_instruction[key])) + except: + exec('map1.' + key + " = " + str(overwrite_instruction[key])) + + def mergeMapping(self, map1, map2): + """ + Merge two mappings recursively. The second mapping is unchanged, + and the first is changed to reflect the results of the merge. + + @param map1: The mapping to merge into. + @type map1: L{Mapping}. + @param map2: The mapping to merge. + @type map2: L{Mapping}. + """ + keys = map1.keys() + global __resolveOverwrite__ + for key in map2.keys(): + if __resolveOverwrite__ and key == "__overwrite__": + self.overwriteKeys(map1,map2[key]) + + elif key not in keys: + map1[key] = map2[key] + if isinstance(map1[key], Container) : + object.__setattr__(map1[key], 'parent', map1) + else: + obj1 = map1[key] + obj2 = map2[key] + decision = self.resolver(map1, map2, key) + if decision == "merge": + self.mergeMapping(obj1, obj2) + elif decision == "append": + self.mergeSequence(obj1, obj2) + elif decision == "overwrite": + map1[key] = obj2 + if isinstance(map1[key], Container): + object.__setattr__(map1[key], 'parent', map1) + elif decision == "mismatch": + self.handleMismatch(obj1, obj2) + else: + msg = "unable to merge: don't know how to implement %r" + raise ValueError(msg % decision) + + def mergeSequence(self, seq1, seq2): + """ + Merge two sequences. The second sequence is unchanged, + and the first is changed to have the elements of the second + appended to it. + + @param seq1: The sequence to merge into. + @type seq1: L{Sequence}. + @param seq2: The sequence to merge. + @type seq2: L{Sequence}. + """ + data1 = object.__getattribute__(seq1, 'data') + data2 = object.__getattribute__(seq2, 'data') + for obj in data2: + data1.append(obj) + comment1 = object.__getattribute__(seq1, 'comments') + comment2 = object.__getattribute__(seq2, 'comments') + for obj in comment2: + comment1.append(obj) + + def handleMismatch(self, obj1, obj2): + """ + Handle a mismatch between two objects. + + @param obj1: The object to merge into. + @type obj1: any + @param obj2: The object to merge. + @type obj2: any + """ + raise ConfigError("unable to merge %r with %r" % (obj1, obj2)) + +class ConfigList(list): + """ + This class implements an ordered list of configurations and allows you + to try getting the configuration from each entry in turn, returning + the first successfully obtained value. + """ + + def getByPath(self, path): + """ + Obtain a value from the first configuration in the list which defines + it. + + @param path: The path of the value to retrieve. + @type path: str + @return: The value from the earliest configuration in the list which + defines it. + @rtype: any + @raise ConfigError: If no configuration in the list has an entry with + the specified path. + """ + found = False + rv = None + for entry in self: + try: + rv = entry.getByPath(path) + found = True + break + except ConfigError: + pass + if not found: + raise ConfigError("unable to resolve %r" % path) + return rv diff --git a/src/common/system.py b/src/common/system.py new file mode 100644 index 0000000..cc8a39f --- /dev/null +++ b/src/common/system.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2013 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +''' +In this file : all functions that do a system call, like open a browser or an editor, or call a git command +''' + +import sys +import subprocess + + +def show_in_editor(editor, filePath): + '''open filePath using editor. + + :param editor str: The editor to use. + :param filePath str: The path to the file to open. + ''' + # default editor is vi + if editor is None or len(editor) == 0: + editor = 'vi' + + if '%s' not in editor: + editor += ' %s' + + try: + # launch cmd using subprocess.Popen + cmd = editor % filePath + p = subprocess.Popen(cmd, shell=True) + p.communicate() + except: + sys.stderr.write("Unable to edit file %s\n" % filePath) + \ No newline at end of file diff --git a/src/config.py b/src/config.py index 9a9bec5..5f18159 100644 --- a/src/config.py +++ b/src/config.py @@ -53,9 +53,9 @@ class ConfigOpener: def __call__(self, name): if os.path.isabs(name): - return common.config_pyconf.ConfigInputStream(open(name, 'rb')) + return common.pyconf.ConfigInputStream(open(name, 'rb')) else: - return common.config_pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') ) + return common.pyconf.ConfigInputStream( open(os.path.join( self.getPath(name), name ), 'rb') ) raise IOError(_("Configuration file '%s' not found") % name) def getPath( self, name ): @@ -96,7 +96,7 @@ class ConfigManager: var['personalDir'] = os.path.join(os.path.expanduser('~'), '.salomeTools') # read linux distributions dictionary - distrib_cfg = common.config_pyconf.Config(os.path.join(var['srcDir'], 'common', 'internal_config', 'distrib.pyconf')) + distrib_cfg = common.pyconf.Config(os.path.join(var['srcDir'], 'common', 'internal_config', 'distrib.pyconf')) # set platform parameters dist_name = common.architecture.get_distribution(codes=distrib_cfg.DISTRIBUTIONS) @@ -168,20 +168,20 @@ class ConfigManager: :param command str: The command that is called. :param dataDir str: The repository that contain external data for salomeTools. :return: The final config. - :rtype: class 'common.config_pyconf.Config' + :rtype: class 'common.pyconf.Config' ''' # create a ConfigMerger to handle merge - merger = common.config_pyconf.ConfigMerger()#MergeHandler()) + merger = common.pyconf.ConfigMerger()#MergeHandler()) # create the configuration instance - cfg = common.config_pyconf.Config() + cfg = common.pyconf.Config() # ======================================================================================= # create VARS section var = self._create_vars(application=application, command=command, dataDir=dataDir) # add VARS to config - cfg.VARS = common.config_pyconf.Mapping(cfg) + cfg.VARS = common.pyconf.Mapping(cfg) for variable in var: cfg.VARS[variable] = var[variable] @@ -192,10 +192,10 @@ class ConfigManager: # ======================================================================================= # Load INTERNAL config # read src/common/internal_config/salomeTools.pyconf - common.config_pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'common', 'internal_config')]) + common.pyconf.streamOpener = ConfigOpener([os.path.join(cfg.VARS.srcDir, 'common', 'internal_config')]) try: - internal_cfg = common.config_pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'common', 'internal_config', 'salomeTools.pyconf'))) - except common.config_pyconf.ConfigError as e: + internal_cfg = common.pyconf.Config(open(os.path.join(cfg.VARS.srcDir, 'common', 'internal_config', 'salomeTools.pyconf'))) + except common.pyconf.ConfigError as e: raise common.SatException(_("Error in configuration file: salomeTools.pyconf\n %(error)s") % \ {'error': str(e) }) @@ -208,10 +208,10 @@ class ConfigManager: # ======================================================================================= # Load SITE config file # search only in the data directory - common.config_pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir]) + common.pyconf.streamOpener = ConfigOpener([cfg.VARS.dataDir]) try: - site_cfg = common.config_pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf'))) - except common.config_pyconf.ConfigError as e: + site_cfg = common.pyconf.Config(open(os.path.join(cfg.VARS.dataDir, 'site.pyconf'))) + except common.pyconf.ConfigError as e: raise common.SatException(_("Error in configuration file: site.pyconf\n %(error)s") % \ {'error': str(e) }) except IOError as error: @@ -235,12 +235,12 @@ class ConfigManager: if application is not None: # search APPLICATION file in all directories in configPath cp = cfg.SITE.config.configPath - common.config_pyconf.streamOpener = ConfigOpener(cp) + common.pyconf.streamOpener = ConfigOpener(cp) try: - application_cfg = common.config_pyconf.Config(application + '.pyconf') + application_cfg = common.pyconf.Config(application + '.pyconf') except IOError as e: raise common.SatException(_("%s, use 'config --list' to get the list of available applications.") %e) - except common.config_pyconf.ConfigError as e: + except common.pyconf.ConfigError as e: raise common.SatException(_("Error in configuration file: %(application)s.pyconf\n %(error)s") % \ { 'application': application, 'error': str(e) } ) @@ -254,15 +254,15 @@ class ConfigManager: # Load softwares config files in SOFTWARE section # The directory containing the softwares definition - softsDir = os.path.join(cfg.VARS.dataDir, 'software_pyconf') + softsDir = os.path.join(cfg.VARS.dataDir, 'softwares') # Loop on all files that are in softsDir directory and read their config for fName in os.listdir(softsDir): if fName.endswith(".pyconf"): - common.config_pyconf.streamOpener = ConfigOpener([softsDir]) + common.pyconf.streamOpener = ConfigOpener([softsDir]) try: - soft_cfg = common.config_pyconf.Config(open(os.path.join(softsDir, fName))) - except common.config_pyconf.ConfigError as e: + soft_cfg = common.pyconf.Config(open(os.path.join(softsDir, fName))) + except common.pyconf.ConfigError as e: raise common.SatException(_("Error in configuration file: %(soft)s\n %(error)s") % \ {'soft' : fName, 'error': str(e) }) except IOError as error: @@ -280,7 +280,7 @@ class ConfigManager: # load USER config self.setUserConfigFile(cfg) user_cfg_file = self.getUserConfigFile() - user_cfg = common.config_pyconf.Config(open(user_cfg_file)) + user_cfg = common.pyconf.Config(open(user_cfg_file)) merger.merge(cfg, user_cfg) # apply overwrite from command line if needed @@ -293,7 +293,7 @@ class ConfigManager: '''Set the user config file name and path. If necessary, build it from another one or create it from scratch. - :param config class 'common.config_pyconf.Config': The global config (containing all pyconf). + :param config class 'common.pyconf.Config': The global config (containing all pyconf). ''' if not config: raise common.SatException(_("Error in setUserConfigFile: config is None")) @@ -347,16 +347,16 @@ class ConfigManager: def createConfigFile(self, config): '''This method is called when there are no user config file. It build it from scratch. - :param config class 'common.config_pyconf.Config': The global config. + :param config class 'common.pyconf.Config': The global config. :return: the config corresponding to the file created. - :rtype: config class 'common.config_pyconf.Config' + :rtype: config class 'common.pyconf.Config' ''' cfg_name = self.getUserConfigFile() - user_cfg = common.config_pyconf.Config() + user_cfg = common.pyconf.Config() # - user_cfg.addMapping('USER', common.config_pyconf.Mapping(user_cfg), "") + user_cfg.addMapping('USER', common.pyconf.Mapping(user_cfg), "") # user_cfg.USER.addMapping('workDir', os.path.expanduser('~'), @@ -395,7 +395,7 @@ class ConfigManager: def print_value(config, path, show_label, level=0, show_full_path=False): '''Prints a value from the configuration. Prints recursively the values under the initial path. - :param config class 'common.config_pyconf.Config': The configuration from which the value is displayed. + :param config class 'common.pyconf.Config': The configuration from which the value is displayed. :param path str : the path in the configuration of the value to print. :param show_label boolean: if True, do a basic display. (useful for bash completion) :param level int: The number of spaces to add before display. @@ -429,7 +429,7 @@ def print_value(config, path, show_label, level=0, show_full_path=False): if show_label: sys.stdout.write("\n") for v in sorted(val.keys()): print_value(config, path + '.' + v, show_label, level + 1) - elif val.__class__ == common.config_pyconf.Sequence or isinstance(val, list): # in this case, value is a list (or a Sequence) + elif val.__class__ == common.pyconf.Sequence or isinstance(val, list): # in this case, value is a list (or a Sequence) if show_label: sys.stdout.write("\n") index = 0 for v in val: @@ -468,13 +468,13 @@ def run(args, runner): editor = runner.cfg.USER.editor if 'APPLICATION' not in runner.cfg: # edit user pyconf usercfg = os.path.join(runner.cfg.VARS.personalDir, 'salomeTools-%s.pyconf'%runner.cfg.INTERNAL['sat_version']) - common.fileSystem.show_in_editor(editor, usercfg) + common.system.show_in_editor(editor, usercfg) else: # search for file .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 .pyconf to ~/.salomeTools/Applications/LOCAL_.pyconf diff --git a/src/salomeTools.py b/src/salomeTools.py index 8fcf244..a120254 100755 --- a/src/salomeTools.py +++ b/src/salomeTools.py @@ -47,7 +47,7 @@ def find_command_list(dirPath): ''' cmd_list = [] for item in os.listdir(dirPath): - if item.endswith('.py') and item!='salomeTools.py': + if item.endswith('.py') and item!='salomeTools.py' and item!='__init__.py': cmd_list.append(item[:-len('.py')]) return cmd_list @@ -64,7 +64,7 @@ parser.add_option('g', 'debug', 'boolean', 'debug_mode', _("run salomeTools in d class salomeTools(object): '''The main class that stores all the commands of salomeTools ''' - def __init__(self, opt, dataDir=None): + def __init__(self, opt='', dataDir=None): '''Initialization :param opt str: The sat options @@ -79,7 +79,7 @@ class salomeTools(object): # initialization of class attributes self.__dict__ = dict() - self.cfg = None # the config that will be read using config_pyconf module + self.cfg = None # the config that will be read using pyconf module self.options = options # the options passed to salomeTools self.dataDir = dataDir # default value will be /data # set the commands by calling the dedicated function @@ -115,7 +115,7 @@ class salomeTools(object): (file_, pathname, description) = imp.find_module(nameCmd, [dirPath]) module = imp.load_module(nameCmd, file_, pathname, description) - def run_command(args): + def run_command(args=''): '''The function that will load the configuration (all pyconf) and return the function run of the command corresponding to module diff --git a/test/__TOOLS__/HTMLTestRunner.py b/test/__TOOLS__/HTMLTestRunner.py deleted file mode 100644 index 0439bf4..0000000 --- a/test/__TOOLS__/HTMLTestRunner.py +++ /dev/null @@ -1,824 +0,0 @@ -""" -A TestRunner for use with the Python unit testing framework. It -generates a HTML report to show the result at a glance. - -The simplest way to use this is to invoke its main method. E.g. - - import unittest - import HTMLTestRunner - - ... define your tests ... - - if __name__ == '__main__': - HTMLTestRunner.main() - - -For more customization options, instantiates a HTMLTestRunner object. -HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. - - # output to a file - fp = file('my_report.html', 'wb') - runner = HTMLTestRunner.HTMLTestRunner( - stream=fp, - title='My unit test', - description='This demonstrates the report output by HTMLTestRunner.' - ) - - # Use an external stylesheet. - # See the Template_mixin class for more customizable options - runner.STYLESHEET_TMPL = '' - - # 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 - -%(heading)s -%(report)s -%(ending)s - - - -""" - # variables: (title, generator, stylesheet, heading, report, ending) - - - # ------------------------------------------------------------------------ - # Stylesheet - # - # alternatively use a for external style sheet, e.g. - # - - STYLESHEET_TMPL = """ - -""" - - - - # ------------------------------------------------------------------------ - # Heading - # - - HEADING_TMPL = """
-

%(title)s

-%(parameters)s -

%(description)s

-
- -""" # variables: (title, parameters, description) - - HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

-""" # variables: (name, value) - - - - # ------------------------------------------------------------------------ - # Report - # - - REPORT_TMPL = """ -

Show -Summary -Failed -All -

- -------- - - - - - - - - -%(test_list)s - - - - - - - - -
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
-""" # variables: (test_list, count, Pass, fail, error) - - REPORT_CLASS_TMPL = r""" - - %(desc)s - %(count)s - %(Pass)s - %(fail)s - %(error)s - Detail - -""" # variables: (style, desc, count, Pass, fail, error, cid) - - - REPORT_TEST_WITH_OUTPUT_TMPL = r""" - -
%(desc)s
- - - - - %(status)s - - - - - - -""" # variables: (tid, Class, style, desc, status) - - - REPORT_TEST_NO_OUTPUT_TMPL = r""" - -
%(desc)s
- %(status)s - -""" # variables: (tid, Class, style, desc, status) - - - REPORT_TEST_OUTPUT_TMPL = r""" -%(id)s: %(output)s -""" # variables: (id, output) - - - - # ------------------------------------------------------------------------ - # ENDING - # - - ENDING_TMPL = """
 
""" - -# -------------------- The end of the Template class ------------------- - - -TestResult = unittest.TestResult - -class _TestResult(TestResult): - # note: _TestResult is a pure representation of results. - # It lacks the output and reporting ability compares to unittest._TextTestResult. - - def __init__(self, verbosity=1): - TestResult.__init__(self) - self.stdout0 = None - self.stderr0 = None - self.success_count = 0 - self.failure_count = 0 - self.error_count = 0 - self.verbosity = verbosity - - # result is a list of result in 4 tuple - # ( - # result code (0: success; 1: fail; 2: error), - # TestCase object, - # Test output (byte string), - # stack trace, - # ) - self.result = [] - - - def startTest(self, test): - TestResult.startTest(self, test) - # just one buffer for both stdout and stderr - self.outputBuffer = StringIO.StringIO() - stdout_redirector.fp = self.outputBuffer - stderr_redirector.fp = self.outputBuffer - self.stdout0 = sys.stdout - self.stderr0 = sys.stderr - sys.stdout = stdout_redirector - sys.stderr = stderr_redirector - - - def complete_output(self): - """ - Disconnect output redirection and return buffer. - Safe to call multiple times. - """ - if self.stdout0: - sys.stdout = self.stdout0 - sys.stderr = self.stderr0 - self.stdout0 = None - self.stderr0 = None - return self.outputBuffer.getvalue() - - - def stopTest(self, test): - # Usually one of addSuccess, addError or addFailure would have been called. - # But there are some path in unittest that would bypass this. - # We must disconnect stdout in stopTest(), which is guaranteed to be called. - self.complete_output() - - - def addSuccess(self, test): - self.success_count += 1 - TestResult.addSuccess(self, test) - output = self.complete_output() - self.result.append((0, test, output, '')) - if self.verbosity > 1: - sys.stderr.write('ok ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('.') - - def addError(self, test, err): - self.error_count += 1 - TestResult.addError(self, test, err) - _, _exc_str = self.errors[-1] - output = self.complete_output() - self.result.append((2, test, output, _exc_str)) - if self.verbosity > 1: - sys.stderr.write('E ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('E') - - def addFailure(self, test, err): - self.failure_count += 1 - TestResult.addFailure(self, test, err) - _, _exc_str = self.failures[-1] - output = self.complete_output() - self.result.append((1, test, output, _exc_str)) - if self.verbosity > 1: - sys.stderr.write('F ') - sys.stderr.write(str(test)) - sys.stderr.write('\n') - else: - sys.stderr.write('F') - - -class HTMLTestRunner(Template_mixin): - """ - """ - def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): - self.stream = stream - self.verbosity = verbosity - if title is None: - self.title = self.DEFAULT_TITLE - else: - self.title = title - if description is None: - self.description = self.DEFAULT_DESCRIPTION - else: - self.description = description - - self.startTime = datetime.datetime.now() - - - def run(self, test): - "Run the given test case or test suite." - result = _TestResult(self.verbosity) - test(result) - self.stopTime = datetime.datetime.now() - self.generateReport(test, result) - print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) - return result - - - def sortResult(self, result_list): - # unittest does not seems to run in any particular order. - # Here at least we want to group them together by class. - rmap = {} - classes = [] - for n,t,o,e in result_list: - cls = t.__class__ - if not rmap.has_key(cls): - rmap[cls] = [] - classes.append(cls) - rmap[cls].append((n,t,o,e)) - r = [(cls, rmap[cls]) for cls in classes] - return r - - - def getReportAttributes(self, result): - """ - Return report attributes as a list of (name, value). - Override this to add custom attributes. - """ - startTime = str(self.startTime)[:19] - duration = str(self.stopTime - self.startTime) - status = [] - if result.success_count: status.append('Pass %s' % result.success_count) - if result.failure_count: status.append('Failure %s' % result.failure_count) - if result.error_count: status.append('Error %s' % result.error_count ) - if status: - status = ' '.join(status) - else: - status = 'none' - return [ - ('Start Time', startTime), - ('Duration', duration), - ('Status', status), - ] - - - def generateReport(self, test, result): - report_attrs = self.getReportAttributes(result) - generator = 'HTMLTestRunner %s' % __version__ - stylesheet = self._generate_stylesheet() - heading = self._generate_heading(report_attrs) - report = self._generate_report(result) - ending = self._generate_ending() - output = self.HTML_TMPL % dict( - title = saxutils.escape(self.title), - generator = generator, - stylesheet = stylesheet, - heading = heading, - report = report, - ending = ending, - ) - self.stream.write(output.encode('utf8')) - - - def _generate_stylesheet(self): - return self.STYLESHEET_TMPL - - - def _generate_heading(self, report_attrs): - a_lines = [] - for name, value in report_attrs: - line = self.HEADING_ATTRIBUTE_TMPL % dict( - name = saxutils.escape(name), - value = saxutils.escape(value), - ) - a_lines.append(line) - heading = self.HEADING_TMPL % dict( - title = saxutils.escape(self.title), - parameters = ''.join(a_lines), - description = saxutils.escape(self.description), - ) - return heading - - - def _generate_report(self, result): - rows = [] - sortedResult = self.sortResult(result.result) - for cid, (cls, cls_results) in enumerate(sortedResult): - # subtotal for a class - np = nf = ne = 0 - for n,t,o,e in cls_results: - if n == 0: np += 1 - elif n == 1: nf += 1 - else: ne += 1 - - # format class description - if cls.__module__ == "__main__": - name = cls.__name__ - else: - name = "%s.%s" % (cls.__module__, cls.__name__) - doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" - desc = doc and '%s: %s' % (name, doc) or name - - row = self.REPORT_CLASS_TMPL % dict( - style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', - desc = desc, - count = np+nf+ne, - Pass = np, - fail = nf, - error = ne, - cid = 'c%s' % (cid+1), - ) - rows.append(row) - - for tid, (n,t,o,e) in enumerate(cls_results): - self._generate_report_test(rows, cid, tid, n, t, o, e) - - report = self.REPORT_TMPL % dict( - test_list = ''.join(rows), - count = str(result.success_count+result.failure_count+result.error_count), - Pass = str(result.success_count), - fail = str(result.failure_count), - error = str(result.error_count), - ) - return report - - - def _generate_report_test(self, rows, cid, tid, n, t, o, e): - # e.g. 'pt1.1', 'ft1.1', etc - has_output = bool(o or e) - tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) - name = t.id().split('.')[-1] - doc = t.shortDescription() or "" - desc = doc and ('%s: %s' % (name, doc)) or name - tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL - - # o and e should be byte string because they are collected from stdout and stderr? - if isinstance(o,str): - # TODO: some problem with 'string_escape': it escape \n and mess up formating - # uo = unicode(o.encode('string_escape')) - uo = o.decode('latin-1') - else: - uo = o - if isinstance(e,str): - # TODO: some problem with 'string_escape': it escape \n and mess up formating - # ue = unicode(e.encode('string_escape')) - ue = e.decode('latin-1') - else: - ue = e - - script = self.REPORT_TEST_OUTPUT_TMPL % dict( - id = tid, - output = saxutils.escape(uo+ue), - ) - - row = tmpl % dict( - tid = tid, - Class = (n == 0 and 'hiddenRow' or 'none'), - style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), - desc = desc, - script = script, - status = self.STATUS[n], - ) - rows.append(row) - if not has_output: - return - - def _generate_ending(self): - return self.ENDING_TMPL - - -############################################################################## -# Facilities for running tests from the command line -############################################################################## - -# Note: Reuse unittest.TestProgram to launch test. In the future we may -# build our own launcher to support more specific command line -# parameters like test title, CSS, etc. -class TestProgram(unittest.TestProgram): - """ - A variation of the unittest.TestProgram. Please refer to the base - class for command line parameters. - """ - def runTests(self): - # Pick HTMLTestRunner as the default test runner. - # base class's testRunner parameter is not useful because it means - # we have to instantiate HTMLTestRunner before we know self.verbosity. - if self.testRunner is None: - self.testRunner = HTMLTestRunner(verbosity=self.verbosity) - unittest.TestProgram.runTests(self) - -main = TestProgram - -############################################################################## -# Executing this module from the command line -############################################################################## - -if __name__ == "__main__": - main(module=None) diff --git a/test/__TOOLS__/tools.py b/test/__TOOLS__/tools.py deleted file mode 100644 index daf88a7..0000000 --- a/test/__TOOLS__/tools.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2012 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import tempfile -import sys -import subprocess - -class outRedirection(): - '''redirection of standart output - useful for testing the terminal display - ''' - def __init__(self): - '''initialization - ''' - self._fstream = tempfile.NamedTemporaryFile(mode='w') - self.saveout = sys.stdout - sys.stdout = self._fstream - - def flush(self): - self._fstream.flush() - - def end_redirection(self): - self._fstream.seek(0) - ff = open(self._fstream.name, 'r') - self.res = ff.read() - self._fstream.close() - sys.stdout = self.saveout - - def read_results(self): - try: - return self.res - except Exception as exc: - print('Problem with redirection : %s' % exc) - sys.exit(1) - -def check_proc_existence(cmd, text_to_find): - - p = subprocess.Popen(cmd, shell=True) - p.communicate() \ No newline at end of file diff --git a/test/_testTools/HTMLTestRunner.py b/test/_testTools/HTMLTestRunner.py new file mode 100644 index 0000000..0439bf4 --- /dev/null +++ b/test/_testTools/HTMLTestRunner.py @@ -0,0 +1,824 @@ +""" +A TestRunner for use with the Python unit testing framework. It +generates a HTML report to show the result at a glance. + +The simplest way to use this is to invoke its main method. E.g. + + import unittest + import HTMLTestRunner + + ... define your tests ... + + if __name__ == '__main__': + HTMLTestRunner.main() + + +For more customization options, instantiates a HTMLTestRunner object. +HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g. + + # output to a file + fp = file('my_report.html', 'wb') + runner = HTMLTestRunner.HTMLTestRunner( + stream=fp, + title='My unit test', + description='This demonstrates the report output by HTMLTestRunner.' + ) + + # Use an external stylesheet. + # See the Template_mixin class for more customizable options + runner.STYLESHEET_TMPL = '' + + # 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 + +%(heading)s +%(report)s +%(ending)s + + + +""" + # variables: (title, generator, stylesheet, heading, report, ending) + + + # ------------------------------------------------------------------------ + # Stylesheet + # + # alternatively use a for external style sheet, e.g. + # + + STYLESHEET_TMPL = """ + +""" + + + + # ------------------------------------------------------------------------ + # Heading + # + + HEADING_TMPL = """
+

%(title)s

+%(parameters)s +

%(description)s

+
+ +""" # variables: (title, parameters, description) + + HEADING_ATTRIBUTE_TMPL = """

%(name)s: %(value)s

+""" # variables: (name, value) + + + + # ------------------------------------------------------------------------ + # Report + # + + REPORT_TMPL = """ +

Show +Summary +Failed +All +

+ ++++++++ + + + + + + + + +%(test_list)s + + + + + + + + +
Test Group/Test caseCountPassFailErrorView
Total%(count)s%(Pass)s%(fail)s%(error)s 
+""" # variables: (test_list, count, Pass, fail, error) + + REPORT_CLASS_TMPL = r""" + + %(desc)s + %(count)s + %(Pass)s + %(fail)s + %(error)s + Detail + +""" # variables: (style, desc, count, Pass, fail, error, cid) + + + REPORT_TEST_WITH_OUTPUT_TMPL = r""" + +
%(desc)s
+ + + + + %(status)s + + + + + + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_NO_OUTPUT_TMPL = r""" + +
%(desc)s
+ %(status)s + +""" # variables: (tid, Class, style, desc, status) + + + REPORT_TEST_OUTPUT_TMPL = r""" +%(id)s: %(output)s +""" # variables: (id, output) + + + + # ------------------------------------------------------------------------ + # ENDING + # + + ENDING_TMPL = """
 
""" + +# -------------------- The end of the Template class ------------------- + + +TestResult = unittest.TestResult + +class _TestResult(TestResult): + # note: _TestResult is a pure representation of results. + # It lacks the output and reporting ability compares to unittest._TextTestResult. + + def __init__(self, verbosity=1): + TestResult.__init__(self) + self.stdout0 = None + self.stderr0 = None + self.success_count = 0 + self.failure_count = 0 + self.error_count = 0 + self.verbosity = verbosity + + # result is a list of result in 4 tuple + # ( + # result code (0: success; 1: fail; 2: error), + # TestCase object, + # Test output (byte string), + # stack trace, + # ) + self.result = [] + + + def startTest(self, test): + TestResult.startTest(self, test) + # just one buffer for both stdout and stderr + self.outputBuffer = StringIO.StringIO() + stdout_redirector.fp = self.outputBuffer + stderr_redirector.fp = self.outputBuffer + self.stdout0 = sys.stdout + self.stderr0 = sys.stderr + sys.stdout = stdout_redirector + sys.stderr = stderr_redirector + + + def complete_output(self): + """ + Disconnect output redirection and return buffer. + Safe to call multiple times. + """ + if self.stdout0: + sys.stdout = self.stdout0 + sys.stderr = self.stderr0 + self.stdout0 = None + self.stderr0 = None + return self.outputBuffer.getvalue() + + + def stopTest(self, test): + # Usually one of addSuccess, addError or addFailure would have been called. + # But there are some path in unittest that would bypass this. + # We must disconnect stdout in stopTest(), which is guaranteed to be called. + self.complete_output() + + + def addSuccess(self, test): + self.success_count += 1 + TestResult.addSuccess(self, test) + output = self.complete_output() + self.result.append((0, test, output, '')) + if self.verbosity > 1: + sys.stderr.write('ok ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('.') + + def addError(self, test, err): + self.error_count += 1 + TestResult.addError(self, test, err) + _, _exc_str = self.errors[-1] + output = self.complete_output() + self.result.append((2, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('E ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('E') + + def addFailure(self, test, err): + self.failure_count += 1 + TestResult.addFailure(self, test, err) + _, _exc_str = self.failures[-1] + output = self.complete_output() + self.result.append((1, test, output, _exc_str)) + if self.verbosity > 1: + sys.stderr.write('F ') + sys.stderr.write(str(test)) + sys.stderr.write('\n') + else: + sys.stderr.write('F') + + +class HTMLTestRunner(Template_mixin): + """ + """ + def __init__(self, stream=sys.stdout, verbosity=1, title=None, description=None): + self.stream = stream + self.verbosity = verbosity + if title is None: + self.title = self.DEFAULT_TITLE + else: + self.title = title + if description is None: + self.description = self.DEFAULT_DESCRIPTION + else: + self.description = description + + self.startTime = datetime.datetime.now() + + + def run(self, test): + "Run the given test case or test suite." + result = _TestResult(self.verbosity) + test(result) + self.stopTime = datetime.datetime.now() + self.generateReport(test, result) + print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) + return result + + + def sortResult(self, result_list): + # unittest does not seems to run in any particular order. + # Here at least we want to group them together by class. + rmap = {} + classes = [] + for n,t,o,e in result_list: + cls = t.__class__ + if not rmap.has_key(cls): + rmap[cls] = [] + classes.append(cls) + rmap[cls].append((n,t,o,e)) + r = [(cls, rmap[cls]) for cls in classes] + return r + + + def getReportAttributes(self, result): + """ + Return report attributes as a list of (name, value). + Override this to add custom attributes. + """ + startTime = str(self.startTime)[:19] + duration = str(self.stopTime - self.startTime) + status = [] + if result.success_count: status.append('Pass %s' % result.success_count) + if result.failure_count: status.append('Failure %s' % result.failure_count) + if result.error_count: status.append('Error %s' % result.error_count ) + if status: + status = ' '.join(status) + else: + status = 'none' + return [ + ('Start Time', startTime), + ('Duration', duration), + ('Status', status), + ] + + + def generateReport(self, test, result): + report_attrs = self.getReportAttributes(result) + generator = 'HTMLTestRunner %s' % __version__ + stylesheet = self._generate_stylesheet() + heading = self._generate_heading(report_attrs) + report = self._generate_report(result) + ending = self._generate_ending() + output = self.HTML_TMPL % dict( + title = saxutils.escape(self.title), + generator = generator, + stylesheet = stylesheet, + heading = heading, + report = report, + ending = ending, + ) + self.stream.write(output.encode('utf8')) + + + def _generate_stylesheet(self): + return self.STYLESHEET_TMPL + + + def _generate_heading(self, report_attrs): + a_lines = [] + for name, value in report_attrs: + line = self.HEADING_ATTRIBUTE_TMPL % dict( + name = saxutils.escape(name), + value = saxutils.escape(value), + ) + a_lines.append(line) + heading = self.HEADING_TMPL % dict( + title = saxutils.escape(self.title), + parameters = ''.join(a_lines), + description = saxutils.escape(self.description), + ) + return heading + + + def _generate_report(self, result): + rows = [] + sortedResult = self.sortResult(result.result) + for cid, (cls, cls_results) in enumerate(sortedResult): + # subtotal for a class + np = nf = ne = 0 + for n,t,o,e in cls_results: + if n == 0: np += 1 + elif n == 1: nf += 1 + else: ne += 1 + + # format class description + if cls.__module__ == "__main__": + name = cls.__name__ + else: + name = "%s.%s" % (cls.__module__, cls.__name__) + doc = cls.__doc__ and cls.__doc__.split("\n")[0] or "" + desc = doc and '%s: %s' % (name, doc) or name + + row = self.REPORT_CLASS_TMPL % dict( + style = ne > 0 and 'errorClass' or nf > 0 and 'failClass' or 'passClass', + desc = desc, + count = np+nf+ne, + Pass = np, + fail = nf, + error = ne, + cid = 'c%s' % (cid+1), + ) + rows.append(row) + + for tid, (n,t,o,e) in enumerate(cls_results): + self._generate_report_test(rows, cid, tid, n, t, o, e) + + report = self.REPORT_TMPL % dict( + test_list = ''.join(rows), + count = str(result.success_count+result.failure_count+result.error_count), + Pass = str(result.success_count), + fail = str(result.failure_count), + error = str(result.error_count), + ) + return report + + + def _generate_report_test(self, rows, cid, tid, n, t, o, e): + # e.g. 'pt1.1', 'ft1.1', etc + has_output = bool(o or e) + tid = (n == 0 and 'p' or 'f') + 't%s.%s' % (cid+1,tid+1) + name = t.id().split('.')[-1] + doc = t.shortDescription() or "" + desc = doc and ('%s: %s' % (name, doc)) or name + tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL + + # o and e should be byte string because they are collected from stdout and stderr? + if isinstance(o,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # uo = unicode(o.encode('string_escape')) + uo = o.decode('latin-1') + else: + uo = o + if isinstance(e,str): + # TODO: some problem with 'string_escape': it escape \n and mess up formating + # ue = unicode(e.encode('string_escape')) + ue = e.decode('latin-1') + else: + ue = e + + script = self.REPORT_TEST_OUTPUT_TMPL % dict( + id = tid, + output = saxutils.escape(uo+ue), + ) + + row = tmpl % dict( + tid = tid, + Class = (n == 0 and 'hiddenRow' or 'none'), + style = n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'none'), + desc = desc, + script = script, + status = self.STATUS[n], + ) + rows.append(row) + if not has_output: + return + + def _generate_ending(self): + return self.ENDING_TMPL + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +# Note: Reuse unittest.TestProgram to launch test. In the future we may +# build our own launcher to support more specific command line +# parameters like test title, CSS, etc. +class TestProgram(unittest.TestProgram): + """ + A variation of the unittest.TestProgram. Please refer to the base + class for command line parameters. + """ + def runTests(self): + # Pick HTMLTestRunner as the default test runner. + # base class's testRunner parameter is not useful because it means + # we have to instantiate HTMLTestRunner before we know self.verbosity. + if self.testRunner is None: + self.testRunner = HTMLTestRunner(verbosity=self.verbosity) + unittest.TestProgram.runTests(self) + +main = TestProgram + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) diff --git a/test/_testTools/tools.py b/test/_testTools/tools.py new file mode 100644 index 0000000..daf88a7 --- /dev/null +++ b/test/_testTools/tools.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import tempfile +import sys +import subprocess + +class outRedirection(): + '''redirection of standart output + useful for testing the terminal display + ''' + def __init__(self): + '''initialization + ''' + self._fstream = tempfile.NamedTemporaryFile(mode='w') + self.saveout = sys.stdout + sys.stdout = self._fstream + + def flush(self): + self._fstream.flush() + + def end_redirection(self): + self._fstream.seek(0) + ff = open(self._fstream.name, 'r') + self.res = ff.read() + self._fstream.close() + sys.stdout = self.saveout + + def read_results(self): + try: + return self.res + except Exception as exc: + print('Problem with redirection : %s' % exc) + sys.exit(1) + +def check_proc_existence(cmd, text_to_find): + + p = subprocess.Popen(cmd, shell=True) + p.communicate() \ No newline at end of file diff --git a/test/config/create_user_pyconf.py b/test/config/create_user_pyconf.py new file mode 100644 index 0000000..4b5a256 --- /dev/null +++ b/test/config/create_user_pyconf.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import unittest +import os +import sys +import shutil + +# get execution path +testdir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(testdir, '..', '..', 'src')) +sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools')) + +from salomeTools import salomeTools +import HTMLTestRunner + +class TestConfig(unittest.TestCase): + '''pyunit class : each method execute one test. + ''' + + def test_option_value(self): + '''Test the display of the right value of "sat config -v VARS.hostname" + ''' + res = "KO" + user_dir = os.path.expanduser(os.path.join('~','.salomeTools')) + user_dir_save = os.path.expanduser(os.path.join('~','.salomeTools_save')) + if os.path.exists(user_dir): + shutil.move(user_dir, user_dir_save) + + # The command to test + sat = salomeTools('') + sat.config('-v .') + + expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'salomeTools-' + sat.cfg.INTERNAL.sat_version + ".pyconf")) + + if os.path.exists(expected_file): + res = "OK" + + shutil.rmtree(user_dir) + shutil.move(user_dir_save, user_dir) + + # pyunit method to compare 2 str + self.assertEqual(res, "OK") + +# test launch +if __name__ == '__main__': + HTMLTestRunner.main() diff --git a/test/config/integration/create_user_pyconf.py b/test/config/integration/create_user_pyconf.py deleted file mode 100644 index 4deb1da..0000000 --- a/test/config/integration/create_user_pyconf.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2012 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import unittest -import os -import sys -import shutil - -# get execution path -testdir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'src')) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__')) - -from salomeTools import salomeTools -import HTMLTestRunner - -class TestConfig(unittest.TestCase): - '''pyunit class : each method execute one test. - ''' - - def test_option_value(self): - '''Test the display of the right value of "sat config -v VARS.hostname" - ''' - res = "KO" - user_dir = os.path.expanduser(os.path.join('~','.salomeTools')) - user_dir_save = os.path.expanduser(os.path.join('~','.salomeTools_save')) - if os.path.exists(user_dir): - shutil.move(user_dir, user_dir_save) - - # The command to test - sat = salomeTools('') - sat.config('-v .') - - expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'salomeTools-' + sat.cfg.INTERNAL.sat_version + ".pyconf")) - - if os.path.exists(expected_file): - res = "OK" - - shutil.rmtree(user_dir) - shutil.move(user_dir_save, user_dir) - - # pyunit method to compare 2 str - self.assertEqual(res, "OK") - -# test launch -if __name__ == '__main__': - HTMLTestRunner.main() \ No newline at end of file diff --git a/test/config/integration/option_copy.py b/test/config/integration/option_copy.py deleted file mode 100644 index 1ad39c6..0000000 --- a/test/config/integration/option_copy.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2012 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import unittest -import os -import sys -import shutil - -# get execution path -testdir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'src')) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__')) - -from salomeTools import salomeTools -import HTMLTestRunner - -class TestConfig(unittest.TestCase): - '''pyunit class : each method execute one test. - ''' - - def test_option_copy(self): - '''Test the copy of a pyconf" - ''' - res = "KO" - appli_to_copy = "appli-test" - - # The command to test - sat = salomeTools('') - sat.config('appli-test -c') - - expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'Application', 'LOCAL_' + appli_to_copy + '.pyconf')) - - if os.path.exists(expected_file): - res = "OK" - os.remove(expected_file) - - # pyunit method to compare 2 str - self.assertEqual(res, "OK") - -# test launch -if __name__ == '__main__': - HTMLTestRunner.main() \ No newline at end of file diff --git a/test/config/integration/option_value.py b/test/config/integration/option_value.py deleted file mode 100644 index 821c76a..0000000 --- a/test/config/integration/option_value.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2012 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import unittest -import os -import sys - -# get execution path -testdir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'src')) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__')) - -from salomeTools import salomeTools -from tools import outRedirection -import HTMLTestRunner - -class TestConfig(unittest.TestCase): - '''pyunit class : each method execute one test. - ''' - - def test_option_value(self): - '''Test the display of the right value of "sat config -v VARS.hostname" - ''' - # expected value - expected = '\x1b[36mhostname\x1b[0m: is221560\n' - - # output redirection - my_out = outRedirection() - - # The command to test - sat = salomeTools('') - sat.config('-v VARS.hostname') - - # stop output redirection - my_out.end_redirection() - - # get results - res = my_out.read_results() - - # pyunit method to compare 2 str - self.assertEqual(res, expected) - - def test_option_list(self): - '''Test the display of the right value of "sat config -l" - ''' - # expected value - expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n' - - # output redirection - my_out = outRedirection() - - # The command to test - sat = salomeTools('') - sat.config('-l') - - # stop output redirection - my_out.end_redirection() - - # get results - res = my_out.read_results() - - # pyunit method to compare 2 str - self.assertEqual(res, expected) - -# test launch -if __name__ == '__main__': - HTMLTestRunner.main() \ No newline at end of file diff --git a/test/config/integration/option_value_2.py b/test/config/integration/option_value_2.py deleted file mode 100644 index 5f43acd..0000000 --- a/test/config/integration/option_value_2.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python -#-*- coding:utf-8 -*- -# Copyright (C) 2010-2012 CEA/DEN -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import unittest -import os -import sys - -# get execution path -testdir = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'src')) -sys.path.append(os.path.join(testdir, '..', '..', '..', 'test', '__TOOLS__')) - -from salomeTools import salomeTools -from tools import outRedirection -import HTMLTestRunner - -class TestConfig(unittest.TestCase): - '''pyunit class : each method execute one test. - ''' - - def test_option_value(self): - '''Test the display of the right value of "sat config -v VARS.hostname" - ''' - # expected value - expected = '\x1b[36mhostname\x1b[0m: is221560\n' - - # output redirection - my_out = outRedirection() - - # The command to test - sat = salomeTools('') - sat.config('-v VARS.python') - - # stop output redirection - my_out.end_redirection() - - # get results - res = my_out.read_results() - - # pyunit method to compare 2 str - self.assertEqual(res, expected) - - def test_option_list(self): - '''Test the display of the right value of "sat config -l" - ''' - # expected value - expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n' - - # output redirection - my_out = outRedirection() - - # The command to test - sat = salomeTools('') - sat.config('-l') - - # stop output redirection - my_out.end_redirection() - - # get results - res = my_out.read_results() - - # pyunit method to compare 2 str - self.assertEqual(res, expected) - -# test launch -if __name__ == '__main__': - HTMLTestRunner.main() \ No newline at end of file diff --git a/test/config/option_copy.py b/test/config/option_copy.py new file mode 100644 index 0000000..cd80b87 --- /dev/null +++ b/test/config/option_copy.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import unittest +import os +import sys +import shutil + +# get execution path +testdir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(testdir, '..', '..', 'src')) +sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools')) + +from salomeTools import salomeTools +import HTMLTestRunner + +class TestConfig(unittest.TestCase): + '''pyunit class : each method execute one test. + ''' + + def test_option_copy(self): + '''Test the copy of a pyconf" + ''' + res = "KO" + appli_to_copy = "appli-test" + + # The command to test + sat = salomeTools('') + sat.config('appli-test -c') + + expected_file = os.path.expanduser(os.path.join('~','.salomeTools', 'Application', 'LOCAL_' + appli_to_copy + '.pyconf')) + + if os.path.exists(expected_file): + res = "OK" + os.remove(expected_file) + + # pyunit method to compare 2 str + self.assertEqual(res, "OK") + +# test launch +if __name__ == '__main__': + HTMLTestRunner.main() diff --git a/test/config/option_value.py b/test/config/option_value.py new file mode 100644 index 0000000..2b1d903 --- /dev/null +++ b/test/config/option_value.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import unittest +import os +import sys + +# get execution path +testdir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(testdir, '..', '..', 'src')) +sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools')) + +from salomeTools import salomeTools +from tools import outRedirection +import HTMLTestRunner + +class TestConfig(unittest.TestCase): + '''pyunit class : each method execute one test. + ''' + + def test_option_value(self): + '''Test the display of the right value of "sat config -v VARS.hostname" + ''' + # expected value + expected = '\x1b[36mhostname\x1b[0m: is221560\n' + + # output redirection + my_out = outRedirection() + + # The command to test + sat = salomeTools('') + sat.config('-v VARS.hostname') + + # stop output redirection + my_out.end_redirection() + + # get results + res = my_out.read_results() + + # pyunit method to compare 2 str + self.assertEqual(res, expected) + + def test_option_list(self): + '''Test the display of the right value of "sat config -l" + ''' + # expected value + expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n' + + # output redirection + my_out = outRedirection() + + # The command to test + sat = salomeTools('') + sat.config('-l') + + # stop output redirection + my_out.end_redirection() + + # get results + res = my_out.read_results() + + # pyunit method to compare 2 str + self.assertEqual(res, expected) + +# test launch +if __name__ == '__main__': + HTMLTestRunner.main() diff --git a/test/config/option_value_2.py b/test/config/option_value_2.py new file mode 100644 index 0000000..ddf0528 --- /dev/null +++ b/test/config/option_value_2.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import unittest +import os +import sys + +# get execution path +testdir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(testdir, '..', '..', 'src')) +sys.path.append(os.path.join(testdir, '..', '..', 'test', '_testTools')) + +from salomeTools import salomeTools +from tools import outRedirection +import HTMLTestRunner + +class TestConfig(unittest.TestCase): + '''pyunit class : each method execute one test. + ''' + + def test_option_value(self): + '''Test the display of the right value of "sat config -v VARS.hostname" + ''' + # expected value + expected = '\x1b[36mhostname\x1b[0m: is221560\n' + + # output redirection + my_out = outRedirection() + + # The command to test + sat = salomeTools('') + sat.config('-v VARS.python') + + # stop output redirection + my_out.end_redirection() + + # get results + res = my_out.read_results() + + # pyunit method to compare 2 str + self.assertEqual(res, expected) + + def test_option_list(self): + '''Test the display of the right value of "sat config -l" + ''' + # expected value + expected = '------ \x1b[34m/home/salome/SPN_PRIVATE/sat5dev_Applications\x1b[0m\nappli-test\n\n------ \x1b[34m/export/home/serioja/.salomeTools/Applications\x1b[0m\n\n' + + # output redirection + my_out = outRedirection() + + # The command to test + sat = salomeTools('') + sat.config('-l') + + # stop output redirection + my_out.end_redirection() + + # get results + res = my_out.read_results() + + # pyunit method to compare 2 str + self.assertEqual(res, expected) + +# test launch +if __name__ == '__main__': + HTMLTestRunner.main() diff --git a/test/run_all.sh b/test/run_all.sh index ccdd9ae..84f8632 100755 --- a/test/run_all.sh +++ b/test/run_all.sh @@ -16,8 +16,8 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -coverage run --source=../src/config.py config/integration/option_value.py > test_res.html -coverage run --source=../src/config.py -a config/integration/option_value_2.py >> test_res.html -coverage run --source=../src/config.py -a config/integration/create_user_pyconf.py >> test_res.html -coverage run --source=../src/config.py -a config/integration/option_copy.py >> test_res.html +coverage run --source=../src/config.py config/option_value.py > test_res.html +coverage run --source=../src/config.py -a config/option_value_2.py >> test_res.html +coverage run --source=../src/config.py -a config/create_user_pyconf.py >> test_res.html +coverage run --source=../src/config.py -a config/option_copy.py >> test_res.html coverage html