Compare commits

...

17 Commits

Author SHA1 Message Date
0e02ae3182 changelog 2019-03-24 20:27:26 +01:00
ac42fd509c remove documentation (now in a separate project) 2019-03-24 20:26:51 +01:00
4709ac5f01 corrections in debug logger 2019-03-23 08:26:39 +01:00
cab8dae15a requirement can have callback 2019-03-13 08:49:18 +01:00
05abe76932 add generic calc_value function 2019-03-09 13:28:15 +01:00
33c1666cc9 add cidr notation to domainnameoption if allow_ip is True (fixes #5) 2019-03-08 07:11:56 +01:00
2a6df8c8d8 safer option.type() (ref #4) 2019-03-06 21:50:28 +01:00
cc44cabf56 update translation 2019-03-05 20:46:04 +01:00
35ee424810 add test_leadership_requires_transitive 2019-03-03 08:02:57 +01:00
5c0ac75c52 multiple option call (fixes #1) 2019-03-02 21:31:21 +01:00
7873910322 doesn't check follower requirement with an other follower or a leader if idx is None (fixes #3) 2019-03-02 19:43:58 +01:00
d25edd7dd7 update test_auto (fixes #2) 2019-03-02 19:32:55 +01:00
e786b4068d update french translation 2019-02-25 20:32:14 +01:00
da015d3af0 coverage 2019-02-25 20:30:20 +01:00
a248e114de simplify netmaskoption 2019-02-25 19:29:00 +01:00
74d367c731 simplify netmaskoption 2019-02-25 19:18:52 +01:00
0e20318a9b simplify netmaskoption 2019-02-25 19:10:20 +01:00
77 changed files with 1960 additions and 3390 deletions

View File

@ -1,4 +1,21 @@
Sat Sep 8 22:54:12 2018 +0200 Emmanuel Garette <egarette@cadoles.com>
Sun Mar 24 20:24:34 2019
* version 3.0 rc3
* corrections in debug logger
* requirement can have callback
* version 3.0 rc2
* add cidr notation to domainnameoption if allow_ip is True (fixes #5)
* safer option.type() (ref #4)
Tue Mar 5 08:22:12 2018 +0200 Emmanuel Garette <egarette@cadoles.com>
* version 3.0 rc2
* simplifying netmaskoption
* multiple option call (fixes #1)
* doesn't check follower requirement with an other follower or a leader if idx is None (fixes #3)
Mon Feb 25 10:12:35 2019 +0200 Emmanuel Garette <egarette@cadoles.com>
* version 3.0 rc1
Sat Sep 8 22:54:12 2019 +0200 Emmanuel Garette <egarette@cadoles.com>
* propose a new API to access to Tiramisu Option
This new API is totally incompatible with older's one

View File

@ -1,153 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
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) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/tiramisu.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/tiramisu.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/tiramisu"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/tiramisu"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View File

@ -1,6 +0,0 @@
{{ fullname }}
{{ underline }}
.. automodule:: {{ fullname }}
:members:
:noindex:

View File

@ -1,15 +0,0 @@
Auto generated library's API
================================
.. autosummary::
:toctree: api
:template: module.rst
tiramisu.option
tiramisu.setting
tiramisu.config
tiramisu.value
tiramisu.autolib
tiramisu.error
tiramisu.storage

View File

@ -1,6 +0,0 @@
tiramisu.autolib
================
.. automodule:: tiramisu.autolib
:members:
:noindex:

View File

@ -1,6 +0,0 @@
tiramisu.config
===============
.. automodule:: tiramisu.config
:members:
:noindex:

View File

@ -1,6 +0,0 @@
tiramisu.error
==============
.. automodule:: tiramisu.error
:members:
:noindex:

View File

@ -1,6 +0,0 @@
tiramisu.option
===============
.. automodule:: tiramisu.option
:members:
:noindex:

View File

@ -1,5 +0,0 @@
tiramisu.setting
================
.. automodule:: tiramisu.setting
:members:

View File

@ -1,6 +0,0 @@
tiramisu.storage
================
.. automodule:: tiramisu.storage
:members:
:noindex:

View File

@ -1,5 +0,0 @@
tiramisu.value
==============
.. automodule:: tiramisu.value
:members:

View File

@ -1,295 +0,0 @@
# -*- coding: utf-8 -*-
#
# tiramisu documentation build configuration file, created by
# sphinx-quickstart on Tue Nov 20 14:29:31 2012.
#
# 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, 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', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.autosummary', 'sphinx.ext.extlinks']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.txt'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'tiramisu'
copyright = u'2013, tiramisu team'
# 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 = '1'
# The full version, including alpha/beta/rc tags.
release = '1.0'
# 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 = ['_build']
# 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.
html_theme = 'traditional'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# 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 = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'tiramisudoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'tiramisu.tex', u'tiramisu Documentation',
u'gwen', '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
# 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', 'tiramisu', u'tiramisu Documentation',
[u'gwen'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'tiramisu', u'tiramisu Documentation',
u'gwen', 'tiramisu', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'tiramisu'
epub_author = u'gwen'
epub_publisher = u'gwen'
epub_copyright = u'2012, gwen'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
todo_include_todos = True
extlinks = {'api': ('./api/tiramisu.%s', ""),
'test': ('./api/test.%s', "")}
autosummary_generate = True

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,257 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="400"
height="200"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="test.svg"
inkscape:export-filename="/home/gnunux/git/tiramisu/doc/storage.png"
inkscape:export-xdpi="135"
inkscape:export-ydpi="135">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3827" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="73.881208"
inkscape:cy="154.11692"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="841"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-852.36218)">
<g
id="g4206"
transform="translate(32.34835,646.56497)">
<text
sodipodi:linespacing="686.00001%"
id="text2985"
y="368.36218"
x="98"
style="font-size:10px;font-style:normal;font-weight:normal;line-height:686.00001335%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="368.36218"
x="98"
id="tspan2987"
sodipodi:role="line">Config</tspan></text>
<rect
y="351.36218"
x="81"
height="30"
width="63"
id="rect3757"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
</g>
<g
id="g4211"
transform="translate(-21.922096,643.64303)">
<rect
y="312.36218"
x="189.5"
height="30"
width="63"
id="rect3757-2"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text3777"
y="325.76599"
x="220.51762"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="325.76599"
x="220.51762"
id="tspan3779"
sodipodi:role="line">Option</tspan><tspan
y="335.76599"
x="220.51762"
sodipodi:role="line"
id="tspan3022">Description</tspan></text>
</g>
<g
id="g4201"
transform="translate(11,622)">
<rect
y="293.42468"
x="81"
height="30"
width="63"
id="rect3757-5"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text4190"
y="309.42468"
x="110.27588"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
id="tspan4194"
y="309.42468"
x="110.27588"
sodipodi:role="line">Option</tspan></text>
</g>
<g
id="g4201-9"
transform="translate(85.749784,621.95117)">
<rect
y="293.42468"
x="81"
height="30"
width="63"
id="rect3757-5-1"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text4190-0"
y="309.42468"
x="110.27588"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
id="tspan4194-2"
y="309.42468"
x="110.27588"
sodipodi:role="line">Option</tspan></text>
</g>
<g
id="g4211-4"
transform="translate(52.525433,602.85429)">
<rect
y="312.36218"
x="189.5"
height="30"
width="63"
id="rect3757-2-3"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text3777-0"
y="325.76599"
x="220.51762"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="325.76599"
x="220.51762"
id="tspan3779-1"
sodipodi:role="line">Option</tspan><tspan
y="335.76599"
x="220.51762"
sodipodi:role="line"
id="tspan3022-7">Description</tspan></text>
</g>
<g
id="g4201-1"
transform="translate(123.6527,582.89051)">
<rect
y="293.42468"
x="81"
height="30"
width="63"
id="rect3757-5-7"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text4190-2"
y="309.42468"
x="110.27588"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
id="tspan4194-8"
y="309.42468"
x="110.27588"
sodipodi:role="line">Option</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 151.43627,945.42468 19.70537,10.58053"
id="path3110"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4201"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g4211"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 198.77217,956.00521 -0.21665,-10.62936"
id="path3112"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4211"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g4201-9"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 226.45587,956.00521 19.69159,-10.78874"
id="path3114"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4211"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g4211-4"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 259.11483,915.21647 -8.55152,-8.90128"
id="path3116"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4211-4"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g4201-1"
inkscape:connection-end-point="d4" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 164.25211,997.92715 15.42203,-11.92194"
id="path3118"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4206"
inkscape:connection-start-point="d4"
inkscape:connection-end="#g4211"
inkscape:connection-end-point="d4" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -1,570 +0,0 @@
.. default-role:: literal
===============================
Options handling basics
===============================
Tiramisu is made of almost three main objects :
- :class:`tiramisu.option.Option` stands for the option types
- :class:`tiramisu.option.OptionDescription` is the shema, the option's structure
- :class:`tiramisu.config.Config` which is the whole configuration entry point
Accessing the `Option`'s
-------------------------
The :class:`~tiramisu.config.Config` object attribute access notation stands for
the value of the configuration's :class:`~tiramisu.option.Option`.
:class:`~tiramisu.config.Config`'s object attribute is the name of the option,
and the value is the value accessed by the `__getattr__` attribute access
mechanism.
If the attribute of the `Config` called by `__getattr__` has not been set before
(by the classic `__setattr__` mechanism), the default value of the `Option`
object is returned, and if no `Option` has been declared in the
`OptionDescription` (that is the schema of the configuration), an
`AttributeError` is raised.
::
>>> from tiramisu.config import Config
>>> from tiramisu.option import BoolOption, OptionDescription
>>>
>>> gcdummy = BoolOption('dummy', 'dummy', default=False)
>>> gcdummy.impl_getdefault()
False
>>> cfg.dummy
False
>>> descr = OptionDescription('tiramisu', '', [gcdummy])
>>> cfg = Config(descr)
>>> cfg.dummy = True
>>> cfg.dummy
True
>>> cfg.idontexist
AttributeError: 'OptionDescription' object has no attribute 'idontexist'
The `Option` objects (in this case the :class:`~tiramisu.option.BoolOption`),
are organized into a tree into nested
:class:`~tiramisu.option.OptionDescription` objects.
.. image:: config.png
Every option has a name, as does every option group. The parts
of the full name of the option are separated by dots: e.g.
``cfg.optgroup.optname``.
Let's make the protocol of accessing a `Config`'s attribute explicit
(because explicit is better than implicit):
1. If the option has not been declared, an `AttributeError` is raised,
2. If an option is declared, but neither a value nor a default value has
been set, the returned value is `None`,
3. If an option is declared and a default value has been set, but no value
has been set, the returned value is the default value of the option,
4. If an option is declared, and a value has been set, the returned value is
the value of the option.
But there are special exceptions. We will see later on that an option can be a
:term:`mandatory option`. A mandatory option is an option that must have a value
defined.
Setting the value of an option
------------------------------
An important part of the setting's configuration consists of setting the
value's option. There are different ways of setting values,
the first one is of course the `__setattr__` method
::
cfg.name = value
And if you wanna come back to a default value, use the builtin `del()` function::
del(cfg.name)
.. module:: tiramisu.config
.. _`tree`:
The handling of options
~~~~~~~~~~~~~~~~~~~~~~~~~~
The handling of options is split into two parts: the description of
which options are available, what their possible values and defaults are
and how they are organized into a tree. A specific choice of options is
bundled into a configuration object which has a reference to its option
description (and therefore makes sure that the configuration values
adhere to the option description).
Common manipulations
------------------------
Let's perform some common manipulation on some options
>>> from tiramisu.config import Config
>>> from tiramisu.option import UnicodeOption, OptionDescription
>>> #
>>> var1 = UnicodeOption('var1', 'first variable')
>>> var2 = UnicodeOption('var2', '', u'value')
>>> #
>>> od1 = OptionDescription('od1', 'first OD', [var1, var2])
>>> rootod = OptionDescription('rootod', '', [od1])
let's set somme access rules on the main namespace
>>> c = Config(rootod)
>>> c.read_write()
let's travel the namespaces
>>> print c
[od1]
>>> print c.od1
var1 = None
var2 = value
>>> print c.od1.var1
None
>>> print c.od1.var2
value
let's modify a value (be careful to the value's type...)
>>> c.od1.var1 = 'value'
Traceback (most recent call last):
ValueError: invalid value value for option var1
>>> c.od1.var1 = u'value'
>>> print c.od1.var1
value
>>> c.od1.var2 = u'value2'
>>> print c.od1.var2
value2
let's come back to the default value
>>> del(c.od1.var2)
>>> print c.od1.var2
value
The value is saved in a :class:`~tiramisu.value.Value` object. It is on this
object that we have to trigger the `reset`, which take the option itself
(`var2`) as a parameter.
On the other side, in the `read_only` mode, it is not possible to modify the value
>>> c.read_only()
>>> c.od1.var2 = u'value2'
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: cannot change the value for option var2 this option is frozen
let's retrieve the option `var1` description
>>> var1.impl_get_information('doc')
'first variable'
And if the option has been lost, it is possible to retrieve it again:
>>> c.unwrap_from_path('od1.var1').impl_get_information('doc')
'first variable'
Searching for an option
~~~~~~~~~~~~~~~~~~~~~~~~~~
In an application, knowing the path of an option is not always feasible.
That's why a tree of options can easily be searched. First, let's build such a tree::
>>> var1 = UnicodeOption('var1', '')
>>> var2 = UnicodeOption('var2', '')
>>> var3 = UnicodeOption('var3', '')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3])
>>> var4 = UnicodeOption('var4', '')
>>> var5 = UnicodeOption('var5', '')
>>> var6 = UnicodeOption('var6', '')
>>> var7 = UnicodeOption('var1', '', u'value')
>>> od2 = OptionDescription('od2', '', [var4, var5, var6, var7])
>>> rootod = OptionDescription('rootod', '', [od1, od2])
>>> c = Config(rootod)
>>> c.read_write()
Second, let's find an option by it's name::
>>> print c.find(byname='var1')
[<tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>,
<tiramisu.option.UnicodeOption object at 0x7ff1b90c7290>]
If the option name is unique, the search can be stopped once one matched option
has been found:
>>> print c.find_first(byname='var1')
<tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>
Instead of the option's object, the value or path can be retrieved:
>>> print c.find(byname='var1', type_='value')
[None, u'value']
>>> print c.find(byname='var1', type_='path')
['od1.var1', 'od2.var1']
Finaly, a search can be performed on the values, the type or even a combination
of all these criteria:
>>> print c.find(byvalue=u'value', type_='path')
['od2.var1']
>>> print c.find(bytype=UnicodeOption, type_='path')
['od1.var1', 'od1.var2', 'od1.var3', 'od2.var4', 'od2.var5', 'od2.var6', 'od2.var1']
>>> print c.find(byvalue=u'value', byname='var1', bytype=UnicodeOption, type_='path')
['od2.var1']
The search can be performed in a subtree:
>>> print c.od1.find(byname='var1', type_='path')
['od1.var1']
In a root tree or in a subtree, all option can be retrieved in a dict container:
>>> print c.make_dict()
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value',
'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
If the organisation in a tree is not important,
:meth:`~config.SubConfig.make_dict()` results can be flattened
>>> print c.make_dict(flatten=True)
{'var5': None, 'var4': None, 'var6': None, 'var1': u'value', 'var3': None,
'var2': None}
.. note:: be carefull with this `flatten` parameter, here we have just lost
two options named `var1`
One can export only interesting parts of a tree of options into a dict, for
example the options that are in the same group that a given `var1` option::
>>> print c.make_dict(withoption='var1')
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value',
'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
>>> print c.make_dict(withoption='var1', withvalue=u'value')
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value'}
and of course, :meth:`~config.SubConfig.make_dict()` can be called in a subtree:
>>> print c.od1.make_dict(withoption='var1')
{'var1': None, 'var3': None, 'var2': None}
The owners
~~~~~~~~~~~
.. glossary::
owner
When a value is set on an option, an owner is set too, that's why one can know
at any time if a value is a default value or not. Let's create a tree::
>>> var1 = UnicodeOption('var1', '', u'oui')
>>> od1 = OptionDescription('od1', '', [var1])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
Then let's retrieve the owner associated to an option::
>>> print c.getowner(var1)
default
>>> c.od1.var1 = u'no'
>>> print c.getowner(var1)
user
>>> del(c.var1)
>>> print c.getowner(var1)
default
You can create your own owner, for example to distinguish modification made by
one user to an other one's.
>>> from tiramisu.setting import owners
>>> owners.addowner('toto')
>>> c.cfgimpl_get_settings().setowner(owners.toto)
>>> print c.getowner(var1)
default
>>> c.od1.var1 = u'no'
>>> print c.getowner(var1)
toto
The properties
~~~~~~~~~~~~~~
A property is an information on an option's state.
Let's create options with properties::
>>> var1 = UnicodeOption('var1', '', u'value', properties=('hidden',))
>>> var2 = UnicodeOption('var2', '', properties=('mandatory',))
>>> var3 = UnicodeOption('var3', '', u'value', properties=('frozen', 'unknown'))
>>> var4 = UnicodeOption('var4', '', u'value')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3])
>>> od2 = OptionDescription('od2', '', [var4], properties=('hidden',))
>>> rootod = OptionDescription('rootod', '', [od1, od2])
>>> c = Config(rootod)
>>> c.read_write()
A hidden value is a value that cannot be accessed in read/write mode. This
option cannot be modified any more. Let's try to access to an option's value
with a hidden option::
>>> print c.od1.var1
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named: var1
with properties ['hidden']
>>> c.read_only()
>>> print c.od1.var1
value
A mandatory option is an option with a value that shall not be `None`. The
value has to be defined. Accessing to such an option is easy in read/write
mode. But in read only mode, an error is raised if no value has been defined::
>>> c.read_write()
>>> print c.od1.var2
None
>>> c.read_only()
>>> print c.od1.var2
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named: var2
with properties ['mandatory']
>>> c.read_write()
>>> c.od1.var2 = u'value'
>>> c.read_only()
>>> print c.od1.var2
value
A frozen option, is an option that cannot be modified by a user.
Let's try to modify a frozen option::
>>> c.read_write()
>>> print c.od1.var3
value
>>> c.od1.var3 = u'value2'
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: cannot change the value for option var3 this option is frozen
>>> c.read_only()
>>> print c.od1.var3
value
Tiramisu allows us to use user defined properties. Let's define and use one in
read/write or read only mode::
>>> c.cfgimpl_get_settings().append('unknown')
>>> print c.od1.var3
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var3 with properties ['unknown']
>>> c.cfgimpl_get_settings().remove('unknown')
>>> print c.od1.var3
value
Many properties can be defined at the same time on an option::
>>> c.cfgimpl_get_settings().extend(['unknown1', 'unknown2'])
Properties can also be defined on an option group (that is, on an
:term:`option description`) let's hide a group and try to access to it::
>>> c.read_write()
>>> print c.od2.var4
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named: od2
with properties ['hidden']
>>> c.read_only()
>>> print c.od2.var4
value
Furthermore, let's retrieve the properties, delete and add the `hidden` property::
>>> c.read_write()
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
['hidden']
>>> print c.od1.var1
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var1 with properties ['hidden']
>>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('hidden')
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
[]
>>> print c.od1.var1
value
>>> c.cfgimpl_get_settings()[rootod.od1.var1].append('hidden')
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
['hidden']
>>> print c.od1.var1
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var1 with properties ['hidden']
.. _multi-option:
The multi-options
~~~~~~~~~~~~~~~~~~~~~
.. glossary::
multi-option
Multi-options are normal options that have list of values (multiple values)
instead of values::
>>> var1 = UnicodeOption('var1', '', [u'val1', u'val2'], multi=True)
>>> od1 = OptionDescription('od1', '', [var1])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
A multi-option's value can be manipulated like a list::
>>> print c.od1.var1
[u'val1', u'val2']
>>> c.od1.var1 = [u'var1']
>>> print c.od1.var1
[u'var1']
>>> c.od1.var1.append(u'val3')
>>> print c.od1.var1
[u'var1', u'val3']
>>> c.od1.var1.pop(1)
u'val3'
>>> print c.od1.var1
[u'var1']
But it is not possible to set a value to a multi-option which is not a list::
>>> c.od1.var1 = u'error'
Traceback (most recent call last):
ValueError: invalid value error for option var1 which must be a list
The master/slave groups
~~~~~~~~~~~~~~~~~~~~~~~~~
.. glossary::
master/slave
A master/slave group is an :class:`~tiramisu.option.OptionDescription` and the
options that lives inside.
Inside this group, a special option, named master option, has the same name as
the group. The group (the option description) is set to type `master`.
All options in a master group is a multi-option (see :ref:`multi-option`).
The slave options have a `default_multi` attribute set to `True`::
>>> from tiramisu.setting import groups
>>> from tiramisu.config import Config
>>> from tiramisu.option import UnicodeOption, OptionDescription
>>>
>>> var1 = UnicodeOption('master', '', multi=True)
>>> var2 = UnicodeOption('slave1', '', multi=True)
>>> var3 = UnicodeOption('slave2', '', multi=True, default_multi=u"default")
>>>
>>> od1 = OptionDescription('master', '', [var1, var2, var3])
>>> od1.impl_set_group_type(groups.master)
>>>
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
The length of the lists can be modified::
>>> print c.master
master = []
slave1 = []
slave2 = []
>>> c.master.master.append(u'oui')
>>> print c.master
master = [u'oui']
slave1 = [None]
slave2 = [u'default']
>>> c.master.master = [u'non']
>>> print c.master
master = [u'non']
slave1 = [None]
slave2 = [u'default']
>>>
>>> c.master.master = [u'oui', u'non']
>>> print c.master
master = [u'oui', u'non']
slave1 = [None, None]
slave2 = [u'default', u'default']
But it is forbidden to change the lenght of a slave::
>>> c.master.slave1[0] = u'super'
>>> print c.master
master = [u'oui', u'non']
slave1 = [u'super', None]
slave2 = [u'default', u'default']
>>> c.master.slave1 = [u'new1', u'new2']
>>> print c.master
master = [u'oui', u'non']
slave1 = [u'new1', u'new2']
slave2 = [u'default', u'default']
>>> c.master.slave1 = [u'new1']
Traceback (most recent call last):
tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
>>> c.master.slave1 = [u'new1', u'new2', u'new3']
tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
you have to call the `pop` function on the master::
>>> c.master.master = [u'oui']
Traceback (most recent call last):
tiramisu.error.SlaveError: invalid len for the master: master which has slave1 as slave with greater len
>>> c.master.master.pop(0)
u'oui'
>>> print c.master
master = [u'non']
slave1 = [u'new2']
slave2 = [u'default']
Configuration's interesting methods
------------------------------------------
A `Config` object is informed by an `option.OptionDescription`
instance. The attributes of the ``Config`` objects are the names of the
children of the ``OptionDescription``.
Here are the (useful) methods on ``Config`` (or `SubConfig`).
.. currentmodule:: tiramisu.config
.. class:: Config
.. autoclass:: SubConfig
:members: find, find_first, __iter__, iter_groups, iter_all, make_dict
.. automethod:: __init__
.. rubric:: Summary
.. autosummary::
find
find_first
__iter__
iter_groups
iter_all
make_dict
.. rubric:: Methods
A :class:`~config.CommonConfig` is a abstract base class. A
:class:`~config.SubConfig` is an just in time created objects that wraps an
::class:`~option.OptionDescription`. A SubConfig differs from a Config in the
fact that a config is a root object and has an environnement, a context which
defines the different properties, access rules, vs... There is generally only
one Config, and many SubConfigs.

View File

@ -1,306 +0,0 @@
.. default-role:: literal
.. currentmodule:: tiramisu
The global consistency
===========================
Identical option names
----------------------
If an :class:`~option.Option()` happens to be defined twice in the
:term:`schema` (e.g. the :class:`~option.OptionDescription()`),
that is the two options actually have the same name, an exception is raised.
The calculation is currently carried out in the samespace, for example
if `config.gc.name` is defined, another option in `gc` with the name
`name` is **not** allowed, whereas `config.whateverelse.name` is still
allowed.
Option's values type validation
--------------------------------
When a value is set to the option, the value is validated by the
option's :class:`option.Option()` validator's type.
Notice that if the option is `multi`, that is the `multi` attribute is set to
`True`, then the validation of the option value accepts a list of values
of the same type.
For example, an :class:`option.IntOption` validator waits for an `int` object of
course, an :class:`option.StrOption` validator waits for an `str`, vs...
Where are located the values
-------------------------------
The entry point of the acces to the values is the :class:`setting.Setting()` of
the root configuration object, but the values are actually located in the
:class:`value.Values()` object, in order to be delegated in some kind of a
`tiramisu.storage`, which can be a in-memory storage, or a persistent (for the
time being, a sqlite3) storage.
:class:`value.Values()` is also responsible of the owners and the calculation
of the options that have callbacks.
Requirements
------------
Configuration options can specify requirements as parameters at the init
time, the specification of some links between options or groups allows
to carry out a dependencies calculation. For example, an option can ben
hidden if another option has been set with some expected value. This is
just an example, the possibilities are hudge.
A requirement is a list of dictionaries that have fairly this form::
[{'option': a, 'expected': False, 'action': 'disabled', 'inverse': True,
'transitive':True, 'same_action': True}]
Actually a transformation is made to this dictionary during the validation of
this requires at the :class:`~option.Option()`'s init. The dictionary becomes
a tuple, wich is passed to the :meth:`~setting.Settings.apply_requires()`
method. Take a look at the code to fully understand the exact meaning of the
requirements:
.. automethod:: tiramisu.setting.Settings.apply_requires
The path of the option is required, the second element is the value wich is
expected to trigger the callback, it is required too, and the third one is the
callback's action name (`hide`, `show`...), wich is a
:class:`~setting.Property()`. Requirements are validated in
:class:`setting.Setting`.
Let's create an option wich has requirements::
>>> from tiramisu.option import *
>>> from tiramisu.config import *
>>> var2 = UnicodeOption('var2', '', u'oui')
>>> var1 = UnicodeOption('var1', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}])
>>> var3 = UnicodeOption('var3', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}, {'option':var2, 'expected':u'non', 'action':'disabled'}])
>>> var4 = UnicodeOption('var4', '', u'oui')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3])
>>> od2 = OptionDescription('od2', '', [var4], requires=[{'option':od1.var2, 'expected':u'oui', 'action':'hidden', 'inverse':True}])
>>> rootod = OptionDescription('rootod', '', [od1, od2])
>>> c = Config(rootod)
>>> c.read_write()
The requirement here is the dict `{'option':var2, 'expected':u'non',
'action':'hidden'}` wich means that is the option `'od1.var2'` is set to
`'non'`, the option `'od1.var1'` is gonna be hidden. On the other hand, if the
option `'od1.var2'` is different from `'non'`, the option `'od1.var1'` is not
hidden any more::
>>> print c.cfgimpl_get_settings()[rootod.od1.var1]
[]
>>> print c.od1.var1
value
>>> print c.od1.var2
oui
>>> c.od1.var2 = u'non'
>>> print c.cfgimpl_get_settings()[rootod.od1.var1]
['hidden']
>>> print c.od1.var1
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var1 with properties ['hidden']
>>> c.od1.var2 = u'oui'
>>> print c.cfgimpl_get_settings()[rootod.od1.var1]
[]
>>> print c.od1.var1
value
The requirement on `od2` is `{'option':od1.var2, 'expected':u'oui',
'action':'hidden', 'inverse':True}`, which means that if the option `od1.var2`
is set to `oui`, the option is not hidden (because of the `True` at the end of
the tuple wich means 'inverted', take a look at the :doc:`consistency`
document.)::
>>> print c.od2.var4
oui
>>> c.od1.var2 = u'non'
>>> print c.od2.var4
Traceback (most recent call last):
tiramisu.error.PropertiesOptionError: trying to access to an option named: od2 with properties ['hidden']
>>> c.od1.var2 = u'oui'
>>> print c.od2.var4
oui
Requirements can be accumulated
>>> print c.cfgimpl_get_settings()[rootod.od1.var3]
[]
>>> c.od1.var2 = u'non'
>>> print c.cfgimpl_get_settings()[rootod.od1.var3]
['disabled', 'hidden']
>>> c.od1.var2 = u'oui'
>>> print c.cfgimpl_get_settings()[rootod.od1.var3]
[]
Requirements can be accumulated for different or identical properties (inverted
or not)::
>>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2,
... 'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui',
... 'action':'hidden'}])
>>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2,
... 'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'excepted':'oui',
... 'action':'disabled', 'inverse':True}])
But it is not possible to have inverted requirements on the same property.
Here is an impossible situation::
>>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2,
... 'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui',
... 'hidden', True}])
Traceback (most recent call last):
ValueError: inconsistency in action types for option: var3 action: hidden
Validation upon a whole configuration object
----------------------------------------------
An option's integrity can be validated towards a whole configuration.
This type of validation is very open. Let's take a use case : an option
has a certain value, and the value of this option can change the owner
of another option or option group... Everything is possible.
.. currentmodule:: tiramisu.option
Other hooks are availables to validate upon a whole configuration at any time,
for example the consistency between two options (typically, an
:class:`IPOption` and a :class:`NetworkOption`).
Let's define validator (wich is a normal python function)::
>>> def valid_a(value, letter=''):
... return value.startswith(letter)
Here is an option wich uses this validator::
>>> var1 = UnicodeOption('var1', '', u'oui', validator=valid_a, validator_args={'letter': 'o'})
>>> od1 = OptionDescription('od1', '', [var1])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
The validation is applied at the modification time::
>>> c.od1.var1 = u'non'
Traceback (most recent call last):
ValueError: invalid value non for option var1
>>> c.od1.var1 = u'oh non'
You can disabled this validation::
>>> c.cfgimpl_get_settings().remove('validator')
>>> c.od1.var1 = u'non'
Values that are calculated
--------------------------------
An option that have a callback is considered to have a value that is to be
calculated.
An option's property with a `force_store_value` attribute is considered to be
modified at the first calculation.
.. automodule:: tiramisu.autolib
:members:
This is the typically protocol for accessing a option's for a calculated value,
but some twisted ways are also possible, take a look at the `force_store_value`
attribute.
.. glossary::
force store value
A calculated value (that is, an option that has a callback) with the
attribute `force_store_value` enabled is considered to be modified at
the first calculation
Let's create four calculation functions::
def return_calc():
#return an unicode value
return u'calc'
def return_value(value):
return value
def return_value_param(param=u''):
return param
def return_no_value_if_non(value):
#if value is not u'non' return value
if value == u'non':
return None
else:
return value
Then we create four options using theses functions::
>>> var1 = UnicodeOption('var1', '', callback=return_calc)
>>> var2 = UnicodeOption('var2', '', callback=return_value, callback_params={'': (u'value',)})
>>> var3 = UnicodeOption('var3', '', callback=return_value_param, callback_params={'param': (u'value_param',)})
>>> var4 = UnicodeOption('var4', '', callback=return_no_value_if_non, callback_params={'': (('od1.var5', False),)})
>>> var5 = UnicodeOption('var5', '', u'oui')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3, var4, var5])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
The first option `var1` returns the result of the `return_calc` function, wich
is `u'calc'`::
>>> print c.od1.var1
calc
The second option `var2` returns the result of the `return_value` fucntion,
wich is `value`. The parameter `u'value'` is passed to this function::
>>> print c.od1.var2
value
The third option `var3` returns the result of the function `return_value_param`
with the named parameter `param` and the value `u'value_param'`::
>>> print c.od1.var3
value_param
The fourth option `var4` returns the reslut of the function `return_no_value_if_non`
that is the value of `od1.var5` exceptif the value is u`non`::
>>> print c.od1.var4
oui
>>> c.od1.var5 = u'new'
>>> print c.od1.var4
new
>>> c.od1.var5 = u'non'
>>> print c.od1.var4
None
The calculation replaces the default value.
If we modify the value, the calculation is not carried out any more::
>>> print c.od1.var1
calc
>>> c.od1.var1 = u'new_value'
>>> print c.od1.var1
new_value
To force the calculation to be carried out in some cases, one must add the
`frozen` and the `force_default_on_freeze` properties::
>>> c.cfgimpl_get_settings()[rootod.od1.var1].append('frozen')
>>> c.cfgimpl_get_settings()[rootod.od1.var1].append('force_default_on_freeze')
>>> print c.od1.var1
calc
>>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('frozen')
>>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('force_default_on_freeze')
>>> print c.od1.var1
new_value

View File

@ -1,92 +0,0 @@
Test framework
==================
Have a look at the :file:`test` subdirectory of the project.
We are using py.test_
.. _py.test: http://pytest.org/latest/
config APIs
-----------------
.. automodule:: test.test_config
:members:
option APIs
---------------
.. automodule:: test.test_option
:members:
others
----------
.. automodule:: test.test_mandatory
:members:
.. automodule:: test.test_config_big_example
:members:
.. automodule:: test.test_option_default
:members:
.. automodule:: test.test_option_consistency
:members:
.. automodule:: test.test_cache
:members:
.. automodule:: test.test_option_setting
:members:
.. automodule:: test.test_freeze
:members:
.. automodule:: test.test_config_ip
:members:
.. automodule:: test.test_slots
:members:
.. automodule:: test.test_reverse_from_path
:members:
.. automodule:: test.test_requires
:members:
.. automodule:: test.test_option_owner
:members:
.. automodule:: test.test_permissive
:members:
.. automodule:: test.test_option_type
:members:
.. automodule:: test.test_dereference
:members:
.. automodule:: test.test_storage
:members:
.. automodule:: test.test_option_calculation
:members:
.. automodule:: test.test_option_with_special_name
:members:
.. automodule:: test.test_config_domain
:members:
.. automodule:: test.test_symlink
:members:
.. automodule:: test.test_metaconfig
:members:
.. automodule:: test.test_parsing_group
:members:

View File

@ -1,12 +0,0 @@
SRC=$(wildcard *.tex)
OBJ=$(subst .tex,.pdf,$(SRC))
pdf: $(OBJ)
%.pdf: %.tex
pdflatex $<
clean:
rm -f $(OBJ)
rm -f *.aux *.log *.toc *.snm *.out *.nav

View File

@ -1,69 +0,0 @@
\begin{frame}
\frametitle{Comparaison entre le noyau de Créole et Tiramisu}
\begin{itemize}
\item \emph{Tiramisu} a pour objectif de
\begin{itemize}
\item remplacer le noyau \emph{Creole} (\texttt{EoleDict}) de manière transparente ;
\item résoudre les problèmes inhérents à \texttt{CreoleServ} ;
\end{itemize}
\item au niveau du code, il y a enfin une vraie séparation du c\oe ur et du fonctionnel ;
\item valide le type \emph{et la structure}, l'ajout de types est aisé.
\item \emph{Creole} : \texttt{EoleDict, EoleVars} $ \Leftrightarrow $ \texttt{Config, Option}\\
cf \texttt{tiramisu/doc/build/pydoc/index.html}
\item intégré à \texttt{gen\_config}, \texttt{cheetah}, \texttt{DTD Creole}, syntaxe \texttt{Creole} \dots
\item \texttt{eole-report/D02CoherenceVariables.pdf}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Gestionnaire de configuration existants}
\begin{itemize}
\item Le gestionnaire de conf de Victor Stinner $\Rightarrow$ \emph{NuFw};
\item puppet, cfgengine... $\Rightarrow$ intéressant, de nombreux comportements peuvent être repris, mais tel quel difficilement compatible avec \emph{Creole};
\item \emph{Creole} $\Leftrightarrow$ \texttt{tiramisu/doc/build/glossary.html}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Un "vrai" serveur de config}
\begin{itemize}
\item un serveur de données de configuration ;
\item $1^{ere}$ méthode : exportation (snapshot) d'un état de la config $ \Rightarrow $ Créole ;
\item $2^{eme}$ méthode : JIT (just in time) calculation, une modification
de l'état de la configuration est possible \emph{pendant} la manipulation et l'utilisation de la conf $ \Rightarrow $ Tiramisu.
\item \texttt{doc/getting-started.html}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Qu'est-ce qu'un gestionnaire de conf moderne ?}
\begin{itemize}
\item c'est une organisation arborescente des données (les données sont imbriquées) ;
\item c'est un accès facile aux données (typiquement une interface de type \emph{dictionnaire}) ;
\item clefs-valeurs, mais quelles valeurs exactement ? $ \Rightarrow $ calcul JIT (just in time) ;
\item \texttt{eole-report/D01AccesVariables.pdf}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Définition d'un gestionnaire de configuration}
\begin{itemize}
\item les families, groups, master \dots~ ce sont des \emph{schémas} de données (\texttt{OptionDescription}) ;
\item c'est la configuration (\texttt{Config}) qui est responsable de l'accès aux valeurs ;
\item la configuration est aisément manipulable, et a un point d'entrée unique ;
\item l'accès aux valeurs des \texttt{Options} de configuration ne peut se faire \emph{que} depuis la conf racine.
\item \texttt{eole-report/D01AccesVariables.pdf}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Organisation en espace de nommage}
\begin{itemize}
\item dans \emph{tiramisu} l'accent est mis sur l'organisation arborescente des données ;
\item la validation des options de configuration se fait par l'appartenance aux groupes (families, master/slaves \dots) ;
\item l'organisation en groupes est unifiée par l'espace de nommage ;
\item \texttt{eole-report/D03ReglesEtats.pdf}
\item lisibilité d'une config : \texttt{tiramisu/report/build/index.html} rapport html d'une config
\end{itemize}
\end{frame}

View File

@ -1,61 +0,0 @@
\begin{frame}
\frametitle{Etats ("status") de la configuration}
\begin{itemize}
\item système d'états de la configuration par \emph{droits d'accès} ;
\item \texttt{read write}, \texttt{read only} ;
\item correspond à \texttt{freeze}, \texttt{hidden}, \texttt{disabled} \dots ;
\item \texttt{doc/status.html} ;
\item \texttt{eole-report/D03ReglesEtats.pdf} ;
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{hidden if in, hidden if not in}
\begin{itemize}
\item les hidden if in, disabled if, \dots sont généralisés
\item dans tiramisu, ce sont des pré-requis sur une (des) variables
\item \texttt{eole-report/D03ReglesEtats.pdf}
\item \texttt{doc/consistency.html}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{un peu de mathématiques : prévenir les deadlocks}
\begin{itemize}
\item sûreté : prévention des deadlocks ;
\item dans tiramisu, le modèle est suffisamment abstrait pour que son exploitation mathématique soit
réalisable par les techniques de \emph{Model Checking} ;
\item soit on a besoin de ne connaître que l'ensemble des états, pas leurs liens $\Rightarrow$ espace d'états ;
\item soit on a besoin de connaître toutes les relations $\Rightarrow$ graphe d'accessibilité ;
\item la configuration est modélisable en une structure de \emph{Kripe} ;
\item déjà le parsing de la conf est facile, la preuve : \texttt{tiramisu/report/build/index.html}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{un peu de mathématiques (suite) CreoleLint}
\begin{itemize}
\item exemple : $ P = 3 \wedge Q = 1 \triangleleft \langle P = 1 \hookleftarrow Q = 0 \rangle$
\item la propriété \og dans aucun état on a $P = 3$ et $Q = 1$ \fg~ est-elle vraie ?
Pour vérifier cette propriété, on a besoin de connaître l'espace d'états ;
\item la propriété \og chaque chemin débutant dans un état accessible $P=1$ passe par un état où $Q=3$ et $P=2$ \fg~
est-elle vraie ? Cela demande de connaître le graphe d'accessibilité ;
\item les structures de \emph{Kripe} sont des machines à états étiquetées par les valuations de toutes les variables propositionnelles ;
\item une compliation statique devient possible dans \emph{CreoleLint} \dots
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{compatibilité Créole : ce qui reste à faire}
\begin{itemize}
\item les options spéciales sont implémentées (auto, fill, obligatoire, \dots) reste la librairie des fonctions pour les variables automatiques \texttt{eosfunc} ;
\item tous les états sont implémentés (hidden, disabled, mode (normal/expert), \dots), il faut fixer les comportement \texttt{read write} ;
\item les "valprec" (valeur précédentes) et une mémoire de \emph{tous} les états antérieurs ;
\item fixer les comportement des hides (sous-groupes récursifs, \dots) ;
\item validations master/slaves, validations globales au regard de la configuration entière puisque c'est possible maintenant.
\end{itemize}
\end{frame}

View File

@ -1,36 +0,0 @@
%%presentation
\documentclass{beamer}
\usepackage{beamerthemetree}
%%impression
%\documentclass[a4paper,9pt]{extarticle}
%\usepackage{beamerarticle}
%%
% class FR
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[frenchb]{babel}
% image
%% \usepackage{graphicx}
\usepackage{alltt}
\usecolortheme{crane}
\beamertemplatetransparentcovered
%\logo{\includegraphics[height=1cm]{ban.png}}
\title{Tiramisu}
\subtitle{gestionnaire de configuration}
\author{Gwen}
\institute{\texttt{git clone git://git.labs.libre-entreprise.org/tiramisu.git} \\
\texttt{firefox tiramisu/doc/build/index.html}}
\date{\today}
\begin{document}
\frame{\titlepage}
\include{definition}
\include{statut}
\end{document}

View File

@ -1,19 +0,0 @@
Errors that may be encountered
==================================
Three builtins exceptions are used :
-----------------------------------------
- **ValueError** : Validation error, parameters error, a list instead
of a Multi, or if the value does not make sense
- **TypeError** : type error in a parameter
- **AttributeError** : wrong path or unknownd option or optiondescription
And five other exceptions :
------------------------------
.. automodule:: tiramisu.error
:members:

View File

@ -1,86 +0,0 @@
==================================
Getting started
==================================
What is options handling ?
=================================
Due to more and more available options required to set up an operating system,
compiler options or whatever, it became quite annoying to hand the necessary
options to where they are actually used and even more annoying to add new
options. To circumvent these problems the configuration control was
introduced...
What is Tiramisu ?
===================
Tiramisu is an options handler and an options controller, which aims at
producing flexible and fast options access. The main advantages are its access
rules and the fact that the whole consistency is preserved at any time, see
:doc:`consistency`. There is of course type and structure validations, but also
validations towards the whole options. Furthermore, options can be reached and
changed according to the access rules from nearly everywhere in your appliance.
Just the facts
==============
.. _gettingtiramisu:
`tiramisu`\ 's home page is here_
.. _here: https://forge.cadoles.com/Cadoles/tiramisu
Download
---------
To obtain a copy of the sources, check it out from the repository using `git`.
We suggest using `git` if one wants to access to the current developments.
::
git clone https://forge.cadoles.com/Cadoles/tiramisu.git
This will get you a fresh checkout of the code repository in a local directory
named ``tiramisu``.
Getting started
-------------------
Option objects can be created in different ways. Let's perform very basic
:class:`~tiramisu.option.Option` and :class:`~tiramisu.config.Config` object
manipulations:
::
>>> from tiramisu.config import Config
>>> from tiramisu.option import OptionDescription, BoolOption
>>> # let's create a group of options... with only one option inside
>>> descr = OptionDescription("optgroup", "", [
... BoolOption("bool", "", default=False)])
>>> # c is a namespace as well as a container for the options
>>> c = Config(descr)
>>> c.bool
False
>>> c.bool = True
>>> c.bool
True
So by now, we have:
- a namespace (which is `c` here)
- the access of an option's value by the
attribute access way (here `bool`, which is a boolean option
:class:`~tiramisu.option.BoolOption()`.
So, option objects are produced at the entry point `c` and then handed down to
where they are actually used when `c.bool` is triggered. This keeps options
local but available at any timer and consistent.
Once the namespace is created, we can set a
:meth:`~config.CommonConfig.read_write()` access to the options::
>>> c.read_write()
which enables us to set a bunch of access rules that we wil explain later in
:doc:`status`.

View File

@ -1,86 +0,0 @@
.. default-role:: literal
Glossary
==========
.. glossary::
configuration
Global configuration object, wich contains the whole configuration
options *and* their descriptions (option types and group)
schema
option description
see :class:`tiramisu.option.OptionDescription`
The schema of a configuration :
- the option types
- how they are organised in groups or even subgroups, that's why we
call them **groups** too.
configuration option
An option object wich has a name and a value and can be accessed
from the configuration object
access rules
Global access rules are : :meth:`~config.CommonConfig.read_write()` or
:meth:`~config.Config.read_only()`, see :doc:`status`
default value
Default value of a configuration option. The default value can be
set at instanciation time, or even at any moment. Remember that if
you reset the default value, the owner reset to `default`
freeze
A whole configuration can be frozen (used in read only access). See
:ref:`frozen` for details.
A single option can be frozen too.
value owner
When an option is modified, including at the instanciation, we
always know who has modified it. It's the owner of the option, see
:doc:`status` for more details.
option with properties
an option wich has property like 'hidden' or 'disabled' is an option
wich has restricted acces rules
hidden option
a hidden option has a different behaviour on regards to the access
of the value in the configuration, see :doc:`status` for more details.
disabled option
a disabled option has a different behaviour on regards to the access
of the value in the configuration, see :doc:`status` for more details.
mandatory option
A mandatory option is a configuration option wich value has to be
set, that is the default value cannot be `None`.
consistency
Preserving the consistency in a whole configuration is a tricky thing,
tiramisu takes care of it for you, see :doc:`consistency` for details.
context
The context is a :class:`tiramisu.setting.Setting()` object in the
configuration that enables us to access to the global properties
for example the `read_write` or `read_only` :term:`access rules`

View File

@ -1,62 +0,0 @@
.. default-role:: literal
.. meta::
:description: configuration management
:keywords: config, configuration
.. title:: tiramisu
The tasting of `Tiramisu`
=========================
.. image:: logo.png
:height: 150px
`Tiramisu`
is a cool, refreshing Italian dessert,
it is also an `options controller tool`_.
.. _`options controller tool`: http://en.wikipedia.org/wiki/Configuration_management#Overview
It's a pretty small, local (that is, straight on the operating system) options
handler and controller.
controlling options explanations
--------------------------------------
.. toctree::
:maxdepth: 1
getting-started
config
option
storage
status
consistency
error
glossary
api
doctest
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
.. note:: The tiramisu code is licensed under the `LGPL licence`_
and this documentation is licensed under the `Creative Commons
Attribution-ShareAlike 3.0 Unported License`_\ .
.. _`Creative Commons Attribution-ShareAlike 3.0 Unported License`: http://creativecommons.org/licenses/by-sa/3.0/deed.en_US
.. _`LGPL licence`: http://www.gnu.org/licenses/lgpl.html
.. todolist::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,125 +0,0 @@
.. default-role:: literal
.. module:: tiramisu.option
The options types
===================
Description of Options
----------------------
All the constructors take a ``name`` and a ``doc`` argument as first
arguments to give to the option or option description a name and a description document.
Most constructors take a ``default`` argument that specifies the default
value of the option. If this argument is not supplied the default value
is assumed to be ``None``.
The `Option` base class
-------------------------
It's the abstract base class for almost all options (except the symlink).
.. _optioninit:
.. autoclass:: Option
:special-members:
:members:
All option types
------------------
BoolOption
~~~~~~~~~~
.. autoclass:: BoolOption
:private-members:
IntOption
~~~~~~~~~
.. autoclass:: IntOption
:private-members:
FloatOption
~~~~~~~~~~~
.. autoclass:: FloatOption
:private-members:
StrOption
~~~~~~~~~
.. autoclass:: StrOption
:private-members:
UnicodeOption
~~~~~~~~~~~~~
.. autoclass:: UnicodeOption
:private-members:
SymLinkOption
~~~~~~~~~~~~~
.. autoclass:: SymLinkOption
:private-members:
``SymLinkOption`` redirects to another configuration option in the
configuration, that is :
- retrieves the value of the target,
- can set the value of the target too
IPOption
~~~~~~~~
.. autoclass:: IPOption
:private-members:
PortOption
~~~~~~~~~~
.. autoclass:: PortOption
:private-members:
NetmaskOption
~~~~~~~~~~~~~
.. autoclass:: NetmaskOption
:private-members:
NetworkOption
~~~~~~~~~~~~~
.. autoclass:: NetworkOption
:private-members:
DomainnameOption
~~~~~~~~~~~~~~~~
.. autoclass:: DomainnameOption
:private-members:
ChoiceOption
~~~~~~~~~~~~
.. autoclass:: ChoiceOption
:private-members:
.. _optdescr:
The `OptionDescription` class
-------------------------------
.. autoclass:: OptionDescription
:special-members:
:members:
If you need to access to an option object, you can do it with the
OptionDescription object. Not only the value of the option by attribute access,
but the option object itself that lives behind the scene. It can always be
accessed internally. The option objects are in the `_children`
`OptionDescription`'s attribute.

View File

@ -1,155 +0,0 @@
.. default-role:: literal
Local statuses and global settings
=====================================
Available configuration statuses
----------------------------------
.. currentmodule:: tiramisu
The configuration's status lives in an :class:`setting.Setting()` object.
This configuration status corresponds to specific attributes or bunch of
attributes that can be accessed together with some `setting.Setting`
method:
**read write status**
The configuration can be accessed by `__get__` and `__set__`
properties, except for the `hidden` configuration options but, yes, it is
possible to modify a disabled option.
To enable read-write status, call
:class:`~config.CommonConfig.read_write()` on the config.
**read only status**
The whole configuration is `frozen`, that is modifiying a value is
forbidden. We can access to a configuration option only with the
`__getattr__` property.
The configuration has not an access to the hidden options
but can read the disabled options.
To enable read only status, call :class:`~config.SubConfig.read_only()` on
the config.
.. csv-table:: **Configuration's statuses summary**
:header: " ", "Hidden", "Disabled", "Mandatory"
"read only status", `False`, `True`, `True`
"read-write status", `True`, `False`, `False`
.. _`frozen`:
Freezing a configuration
---------------------------
At the configuration level, :class:`~setting.Setting()` enables us to freeze the
whole configuration, wich means that the frozen :class:`~option.Option()`'s
values cannot be modified.
It is possible to *freeze* a single :class:`~option.Option()` object with
:meth:`~config.SubConfig.cfgimpl_get_settings()`. If you try to modify a frozen
option, it raises a `TypeError: trying to change a frozen option object`.
To find out if an option `myoption` is frozen, just make an assertion on the
settings like that::
'frozen' in cfg.cfgimpl_get_settings()[myoption]
Moreover, frozen option can return their default values if
:meth:`~option.Option.force_default()` is called on this option.
.. glossary::
force default on freeze
A single option is frozen and we want the option to return something
else than his value, typically it is his default value.
In the option's values, an attribute can be set
:attr:`force_default_on_freeze`, that forces this behavior.
Restricted access to an `Option()`
-----------------------------------
.. autoclass:: tiramisu.setting.Property
The `properties` attribute is in charge of the access rules' option's value.
Configuration options access statuses are defined at configuration level
that corresponds to the `option.Option()`'s `properties` attribute,
for example: hidden, disabled.
Use the pythonic way to know if a property is there::
'hidden' in c.cfgimpl_get_settings()
'frozen' in c.cfgimpl_get_settings()[opt]
To access to the global settings::
cfg.cfgimpl_get_settings()
cfg.cfgimpl_get_settings()[option1]
to add, or suppress a global property::
cfg.cfgimpl_get_settings()[option1].append('hidden')
cfg.cfgimpl_get_settings()[option1].remove('hidden')
to activate, deactivate properties::
cfg.cfgimpl_get_settings().append('hidden')
cfg.cfgimpl_get_settings().remove('hidden')
The global properties are living in e :class:`~setting.Setting` object. A
`Setting` object also takes care of the way to access option values and the
storage in the cache.
Value owners
-------------
Every configuration option has a **owner**. When the option is instanciated,
the owner is :obj:`setting.owners.default` because a default value has been set
(including `None`, wich means that no value has been set yet).
.. method:: config.CommonConfig.getowner()
This method can retrieve an Option's owner.
- At the instance of the `Config` object, the value owner is
:obj:`setting.owners.default` because
the default values are set at the instance of the configuration option
object,
- at the modification of an option, the owner is `owners.default`, (which is
:obj:`owners.user`)
Special behaviors for an option
---------------------------------
**mandatory**
A mandatory option shall return a value. If a value, or a default value
has not been set, a error is raised.
**has a callback**
This means that it is a calculated value and therefore automatically
protected it cannot be modified by attribute access.
**force_store_value**
if the configuration option has a default value, the default is
returned, otherwise the value is calculated.
Its inner state is represented by `option.Option.force_default()`
Configuration options have default values that are stored in the
`Option()` object itself. Default values, the `default`, can be set in
various ways.
If a default value is modified by overriding it, not only the value of
the option resets to the default that is proposed, but the owner is
modified too, it is reseted to `owners.default`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,265 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="400"
height="200"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="test.svg"
inkscape:export-filename="/home/gnunux/git/tiramisu/doc/storage.png"
inkscape:export-xdpi="135"
inkscape:export-ydpi="135">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective3827" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="106.95445"
inkscape:cy="208.15932"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1600"
inkscape:window-height="841"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-852.36218)">
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 235.5,78.588237 306,109"
id="path4403"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4211"
inkscape:connection-start-point="d4"
transform="translate(0,852.36218)" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 235.5,131.08416 305,107"
id="path4405"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0"
inkscape:connection-start="#g4216"
inkscape:connection-start-point="d4"
transform="translate(0,852.36218)" />
<g
id="g4206"
transform="translate(-17,590)">
<text
sodipodi:linespacing="686.00001%"
id="text2985"
y="368.36218"
x="98"
style="font-size:10px;font-style:normal;font-weight:normal;line-height:686.00001335%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="368.36218"
x="98"
id="tspan2987"
sodipodi:role="line">Config</tspan></text>
<rect
y="351.36218"
x="81"
height="30"
width="63"
id="rect3757"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
</g>
<g
id="g4211"
transform="translate(-17,590)">
<rect
y="312.36218"
x="189.5"
height="30"
width="63"
id="rect3757-2"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="686.00001%"
id="text3777"
y="330.36218"
x="206"
style="font-size:10px;font-style:normal;font-weight:normal;line-height:686.00001335%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="330.36218"
x="206"
id="tspan3779"
sodipodi:role="line">Values</tspan></text>
</g>
<g
id="g4216"
transform="translate(-17,590)">
<rect
y="389.36218"
x="189.5"
height="30"
width="63"
id="rect3757-4"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="686.00001%"
id="text3799"
y="407.36218"
x="200"
style="font-size:10px;font-style:normal;font-weight:normal;line-height:686.00001335%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="407.36218"
x="200"
id="tspan3801"
sodipodi:role="line">Settings</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 127,967.39444 45.5,15.93548"
id="path4028"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 127,945.0396 45.5,-16.35484"
id="path4030"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="rect4161"
width="55.5"
height="26"
x="277.5"
y="946.36218" />
<path
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:1.96347165;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3843"
sodipodi:cx="401"
sodipodi:cy="334.86218"
sodipodi:rx="38"
sodipodi:ry="10.5"
d="m 439,334.86218 a 38,10.5 0 1 1 -76,0 38,10.5 0 1 1 76,0 z"
transform="matrix(0.71325325,0,0,0.57998971,18.66254,749.17042)" />
<path
transform="matrix(0.71325325,0,0,0.57998971,18.57337,775.05247)"
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:1.96347165;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3843-3"
sodipodi:cx="401"
sodipodi:cy="334.86218"
sodipodi:rx="38"
sodipodi:ry="10.5"
d="m 439,334.86218 a 38,10.5 0 1 1 -76,0 38,10.5 0 1 1 76,0 z" />
<path
transform="matrix(0.71325325,0,0,0.57998971,18.52879,762.07519)"
sodipodi:type="arc"
style="fill:none;stroke:#000000;stroke-width:1.96347165;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3843-3-0"
sodipodi:cx="401"
sodipodi:cy="334.86218"
sodipodi:rx="38"
sodipodi:ry="10.5"
d="m 439,334.86218 a 38,10.5 0 1 1 -76,0 38,10.5 0 1 1 76,0 z" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="rect3883"
width="62.989182"
height="6.7061315"
x="274.72043"
y="949.91193" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none"
id="rect3883-3"
width="58.087975"
height="6.4161367"
x="277.34818"
y="962.78046" />
<path
style="fill:none;stroke:#000000;stroke-width:1.26286423;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
d="m 277.52869,943.35095 -0.0442,26.02673"
id="path3917"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:1.26286423;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
d="m 331.64698,969.26909 0.13377,-26.17203"
id="path3921"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:10px;font-style:normal;font-weight:normal;line-height:686.00001335%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="286.33643"
y="958.32324"
id="text3821"
sodipodi:linespacing="686.00001%"><tspan
sodipodi:role="line"
id="tspan3823"
x="286.33643"
y="958.32324">Storage</tspan></text>
<g
id="g4201"
transform="translate(-17,590)">
<rect
y="293.42468"
x="81"
height="30"
width="63"
id="rect3757-5"
style="fill:none;stroke:#000000;stroke-linejoin:round;stroke-opacity:1" />
<text
sodipodi:linespacing="100%"
id="text4190"
y="309.42468"
x="110.27588"
style="font-size:10px;font-style:normal;font-weight:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
id="tspan4194"
y="309.42468"
x="110.27588"
sodipodi:role="line">Option</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 95.5,913.42468 0,27.9375"
id="path4199"
inkscape:connector-type="polyline"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -1,52 +0,0 @@
Storage
=======
.. automodule:: tiramisu.storage
.. automethod:: tiramisu.storage.set_storage
.. image:: storage.png
Dictionary
~~~~~~~~~~
.. automodule:: tiramisu.storage.dictionary
Dictionary settings:
.. automethod:: tiramisu.storage.dictionary.storage.Setting
Sqlite3
~~~~~~~
.. automodule:: tiramisu.storage.sqlite3
Sqlite3 settings:
.. automethod:: tiramisu.storage.sqlite3.storage.Setting
Example
~~~~~~~
>>> from tiramisu.option import StrOption, OptionDescription
>>> from tiramisu.config import Config
>>> from tiramisu.storage import set_storage
>>> set_storage('sqlite3', dir_database='/tmp/tiramisu')
>>> s = StrOption('str', '')
>>> o = OptionDescription('od', '', [s])
>>> c1 = Config(o, persistent=True, session_id='xxxx')
>>> c1.str
>>> c1.str = 'yes'
>>> c1.str
'yes'
>>> del(c1)
>>> c2 = Config(o, persistent=True, session_id='xxxx')
>>> c2.str
'yes'
>>> del(c2)
>>> list_sessions()
['xxxx']
>>> delete_session('xxxx')
>>> c3 = Config(o, persistent=True, session_id='xxxx')
>>> c3.str

View File

@ -83,30 +83,34 @@ def _autocheck_default_value(cfg, path, conf, **kwargs):
# test default value (should be empty)
# cannot test for follower (we cannot get all values for a follower)
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
with warnings.catch_warnings(record=True) as w:
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(path).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(path).value.get() == empty_value
assert cfg_.option(path).value.get() == empty_value
assert cfg_.forcepermissive.option(path).value.get() == empty_value
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(path).value.get()")
assert cfg.config(conf).forcepermissive.option(path).value.get() == empty_value
raises(PropertiesOptionError, "cfg_.option(path).value.get()")
assert cfg_.forcepermissive.option(path).value.get() == empty_value
else:
raises(PropertiesOptionError, "cfg.config(conf).option(path).value.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(path).value.get()")
raises(PropertiesOptionError, "cfg_.option(path).value.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(path).value.get()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(path, 0).value.get() == empty_value
assert cfg.config(conf).option(path, 1).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(path, 0).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(path, 1).value.get() == empty_value
assert cfg_.option(path, 0).value.get() == empty_value
assert cfg_.option(path, 1).value.get() == empty_value
assert cfg_.forcepermissive.option(path, 0).value.get() == empty_value
assert cfg_.forcepermissive.option(path, 1).value.get() == empty_value
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(path, 0).value.get()")
assert cfg.config(conf).forcepermissive.option(path, 0).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(path, 1).value.get() == empty_value
raises(PropertiesOptionError, "cfg_.option(path, 0).value.get()")
assert cfg_.forcepermissive.option(path, 0).value.get() == empty_value
assert cfg_.forcepermissive.option(path, 1).value.get() == empty_value
else:
raises(PropertiesOptionError, "cfg.config(conf).option(path, 0).value.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(path, 0).value.get()")
raises(PropertiesOptionError, "cfg_.option(path, 0).value.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(path, 0).value.get()")
def _set_value(cfg, pathwrite, conf, **kwargs):
@ -131,52 +135,56 @@ def _set_value(cfg, pathwrite, conf, **kwargs):
# for follower should have an index and good length
# for leader must append, not set
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
with warnings.catch_warnings(record=True) as w:
if isleader:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
raises(APIError, "cfg.config(conf).option(pathwrite, 0).value.set(first_value[0])")
raises(APIError, "cfg_.option(pathwrite, 0).value.set(first_value[0])")
if not set_permissive:
cfg.config(conf).option(pathwrite).value.set([first_value[0]])
cfg_.option(pathwrite).value.set([first_value[0]])
else:
cfg.config(conf).forcepermissive.option(pathwrite).value.set([first_value[0]])
cfg_.forcepermissive.option(pathwrite).value.set([first_value[0]])
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite).value.set([first_value[0]])")
raises(PropertiesOptionError, "cfg_.option(pathwrite).value.set([first_value[0]])")
if set_permissive:
cfg.config(conf).forcepermissive.option(pathwrite).value.set([first_value[0]])
cfg_.forcepermissive.option(pathwrite).value.set([first_value[0]])
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite).value.set([first_value[0]])")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathwrite).value.set([first_value[0]])")
raises(PropertiesOptionError, "cfg_.option(pathwrite).value.set([first_value[0]])")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathwrite).value.set([first_value[0]])")
if len(first_value) > 1:
raises(APIError, "cfg.config(conf).unrestraint.option(pathwrite).value.set(first_value[1])")
raises(APIError, "cfg_.unrestraint.option(pathwrite).value.set(first_value[1])")
elif isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
if not set_permissive:
cfg.config(conf).option(pathwrite, 1).value.set(second_value)
cfg_.option(pathwrite, 1).value.set(second_value)
else:
cfg.config(conf).forcepermissive.option(pathwrite, 1).value.set(second_value)
cfg_.forcepermissive.option(pathwrite, 1).value.set(second_value)
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite, 1).value.set(second_value)")
raises(PropertiesOptionError, "cfg_.option(pathwrite, 1).value.set(second_value)")
if set_permissive:
cfg.config(conf).forcepermissive.option(pathwrite, 1).value.set(second_value)
cfg_.forcepermissive.option(pathwrite, 1).value.set(second_value)
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite, 1).value.set(second_value)")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathwrite, 1).value.set(second_value)")
raises(APIError, "cfg.config(conf).unrestraint.option(pathwrite).value.set([second_value, second_value])")
raises(PropertiesOptionError, "cfg_.option(pathwrite, 1).value.set(second_value)")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathwrite, 1).value.set(second_value)")
raises(APIError, "cfg_.unrestraint.option(pathwrite).value.set([second_value, second_value])")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
if not set_permissive:
cfg.config(conf).option(pathwrite).value.set(first_value)
cfg_.option(pathwrite).value.set(first_value)
else:
cfg.config(conf).forcepermissive.option(pathwrite).value.set(first_value)
cfg_.forcepermissive.option(pathwrite).value.set(first_value)
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite).value.set(first_value)")
raises(PropertiesOptionError, "cfg_.option(pathwrite).value.set(first_value)")
if set_permissive:
cfg.config(conf).forcepermissive.option(pathwrite).value.set(first_value)
cfg_.forcepermissive.option(pathwrite).value.set(first_value)
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathwrite).value.set(first_value)")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathwrite).value.set(first_value)")
#FIXME raises(APIError, "cfg.config(conf).unrestraint.option(pathwrite).value.set(first_value)")
raises(PropertiesOptionError, "cfg_.option(pathwrite).value.set(first_value)")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathwrite).value.set(first_value)")
#FIXME raises(APIError, "cfg_.unrestraint.option(pathwrite).value.set(first_value)")
def _getproperties(multi, isfollower, kwargs):
@ -195,37 +203,41 @@ def _getproperties(multi, isfollower, kwargs):
def _check_properties(cfg, mcfg, pathread, conf, kwargs, props_permissive, props):
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if not cfg.unrestraint.option(pathread).option.isfollower():
if not kwargs.get('permissive_od', False):
assert set(cfg.config(conf).option(pathread).property.get()) == set(props_permissive)
assert set(cfg.config(conf).option(pathread).property.get()) == set(props)
assert set(cfg_.option(pathread).property.get()) == set(props_permissive)
assert set(cfg_.option(pathread).property.get()) == set(props)
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).property.get()")
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).property.get()")
assert set(cfg.config(conf).forcepermissive.option(pathread).property.get()) == set(props_permissive)
assert set(cfg.config(conf).forcepermissive.option(pathread).property.get()) == set(props)
assert set(cfg.config(conf).unrestraint.option(pathread).property.get()) == set(props_permissive)
assert set(cfg.config(conf).unrestraint.option(pathread).property.get()) == set(props)
raises(PropertiesOptionError, "cfg_.option(pathread).property.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).property.get()")
assert set(cfg_.forcepermissive.option(pathread).property.get()) == set(props_permissive)
assert set(cfg_.forcepermissive.option(pathread).property.get()) == set(props)
assert set(cfg_.unrestraint.option(pathread).property.get()) == set(props_permissive)
assert set(cfg_.unrestraint.option(pathread).property.get()) == set(props)
else:
if not kwargs.get('permissive_od', False):
assert set(cfg.config(conf).option(pathread, 0).property.get()) == set(props_permissive)
assert set(cfg.config(conf).option(pathread, 0).property.get()) == set(props)
assert set(cfg_.option(pathread, 0).property.get()) == set(props_permissive)
assert set(cfg_.option(pathread, 0).property.get()) == set(props)
#
assert set(cfg.config(conf).option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg.config(conf).option(pathread, 1).property.get()) == set(props)
assert set(cfg_.option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg_.option(pathread, 1).property.get()) == set(props)
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).property.get()")
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).property.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 0).property.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 0).property.get()")
#
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 1).property.get()")
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 1).property.get()")
assert set(cfg.config(conf).forcepermissive.option(pathread, 0).property.get()) == set(props_permissive)
assert set(cfg.config(conf).forcepermissive.option(pathread, 0).property.get()) == set(props)
raises(PropertiesOptionError, "cfg_.option(pathread, 1).property.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 1).property.get()")
assert set(cfg_.forcepermissive.option(pathread, 0).property.get()) == set(props_permissive)
assert set(cfg_.forcepermissive.option(pathread, 0).property.get()) == set(props)
#
assert set(cfg.config(conf).forcepermissive.option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg.config(conf).forcepermissive.option(pathread, 1).property.get()) == set(props)
assert set(cfg.config(conf).unrestraint.option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg.config(conf).unrestraint.option(pathread, 1).property.get()) == set(props)
assert set(cfg_.forcepermissive.option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg_.forcepermissive.option(pathread, 1).property.get()) == set(props)
assert set(cfg_.unrestraint.option(pathread, 1).property.get()) == set(props_permissive)
assert set(cfg_.unrestraint.option(pathread, 1).property.get()) == set(props)
def _property_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kwargs):
@ -253,12 +265,16 @@ def _property_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
# set properties with permissive
for prop in properties:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
cfg.config(confread).option(pathwrite).property.add(prop)
elif not kwargs.get('propertyerror', False):
cfg.config(confread).forcepermissive.option(pathwrite).property.add(prop)
if confread is not None:
cfg_ = cfg.config(confread)
else:
cfg.config(confread).unrestraint.option(pathwrite).property.add(prop)
cfg_ = cfg
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
cfg_.option(pathwrite).property.add(prop)
elif not kwargs.get('propertyerror', False):
cfg_.forcepermissive.option(pathwrite).property.add(prop)
else:
cfg_.unrestraint.option(pathwrite).property.add(prop)
if confwrite == confread:
_check_properties(cfg, mcfg, pathread, confwrite, kwargs, properties, properties)
else:
@ -286,64 +302,72 @@ def _autocheck_get_value(cfg, pathread, conf, **kwargs):
second_value = SUBLIST_SECOND_VALUE[1]
# get value after set value without permissive
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
with warnings.catch_warnings(record=True) as w:
if isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread, 0).value.get() == empty_value
assert cfg.config(conf).option(pathread, 1).value.get() == second_value
assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == second_value
assert cfg_.option(pathread, 0).value.get() == empty_value
assert cfg_.option(pathread, 1).value.get() == second_value
assert cfg_.forcepermissive.option(pathread, 0).value.get() == empty_value
assert cfg_.forcepermissive.option(pathread, 1).value.get() == second_value
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).value.get()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get() == empty_value
raises(PropertiesOptionError, "cfg_.option(pathread, 0).value.get()")
assert cfg_.forcepermissive.option(pathread, 0).value.get() == empty_value
if set_permissive:
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == second_value
assert cfg_.forcepermissive.option(pathread, 1).value.get() == second_value
else:
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == empty_value
assert cfg_.forcepermissive.option(pathread, 1).value.get() == empty_value
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).value.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread, 0).value.get()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread).value.get() == first_value
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == first_value
assert cfg_.option(pathread).value.get() == first_value
assert cfg_.forcepermissive.option(pathread).value.get() == first_value
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).value.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).value.get()")
if set_permissive:
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == first_value
assert cfg_.forcepermissive.option(pathread).value.get() == first_value
else:
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == empty_value
assert cfg_.forcepermissive.option(pathread).value.get() == empty_value
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).value.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).value.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).value.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread).value.get()")
def _check_owner(cfg, pathread, conf, kwargs, owner, permissive_owner):
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread).owner.get() == owner
assert cfg.config(conf).forcepermissive.option(pathread).owner.get() == owner
assert cfg_.option(pathread).owner.get() == owner
assert cfg_.forcepermissive.option(pathread).owner.get() == owner
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.get()")
assert cfg.config(conf).forcepermissive.option(pathread).owner.get() == permissive_owner
raises(PropertiesOptionError, "cfg_.option(pathread).owner.get()")
assert cfg_.forcepermissive.option(pathread).owner.get() == permissive_owner
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread).owner.get()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread, 0).owner.get() == 'default'
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.get() == 'default'
assert cfg.config(conf).option(pathread, 1).owner.get() == owner
assert cfg.config(conf).forcepermissive.option(pathread, 1).owner.get() == owner
assert cfg_.option(pathread, 0).owner.get() == 'default'
assert cfg_.forcepermissive.option(pathread, 0).owner.get() == 'default'
assert cfg_.option(pathread, 1).owner.get() == owner
assert cfg_.forcepermissive.option(pathread, 1).owner.get() == owner
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 1).owner.get()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.get() == 'default'
assert cfg.config(conf).forcepermissive.option(pathread, 1).owner.get() == permissive_owner
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 1).owner.get()")
assert cfg_.forcepermissive.option(pathread, 0).owner.get() == 'default'
assert cfg_.forcepermissive.option(pathread, 1).owner.get() == permissive_owner
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread, 0).owner.get()")
@autocheck
@ -373,49 +397,53 @@ def autocheck_default_owner(cfg, mcfg, pathread, pathwrite, confread, confwrite,
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
# check if owner is a string "default" and 'isdefault'
def do(conf):
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread).owner.get() == 'default'
assert cfg.config(conf).forcepermissive.option(pathread).owner.get() == 'default'
assert cfg_.option(pathread).owner.get() == 'default'
assert cfg_.forcepermissive.option(pathread).owner.get() == 'default'
#
assert cfg.config(conf).option(pathread).owner.isdefault()
assert cfg.config(conf).forcepermissive.option(pathread).owner.isdefault()
assert cfg_.option(pathread).owner.isdefault()
assert cfg_.forcepermissive.option(pathread).owner.isdefault()
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.get()")
assert cfg.config(conf).forcepermissive.option(pathread).owner.get() == 'default'
raises(PropertiesOptionError, "cfg_.option(pathread).owner.get()")
assert cfg_.forcepermissive.option(pathread).owner.get() == 'default'
#
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.isdefault()")
assert cfg.config(conf).forcepermissive.option(pathread).owner.isdefault()
raises(PropertiesOptionError, "cfg_.option(pathread).owner.isdefault()")
assert cfg_.forcepermissive.option(pathread).owner.isdefault()
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).owner.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread).owner.get()")
#
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).owner.isdefault()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).owner.isdefault()")
raises(PropertiesOptionError, "cfg_.option(pathread).owner.isdefault()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread).owner.isdefault()")
#
assert cfg.config(conf).unrestraint.option(pathread).owner.get() == 'default'
assert cfg.config(conf).unrestraint.option(pathread).owner.isdefault()
assert cfg_.unrestraint.option(pathread).owner.get() == 'default'
assert cfg_.unrestraint.option(pathread).owner.isdefault()
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread, 0).owner.get() == 'default'
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.get() == 'default'
assert cfg_.option(pathread, 0).owner.get() == 'default'
assert cfg_.forcepermissive.option(pathread, 0).owner.get() == 'default'
#
assert cfg.config(conf).option(pathread, 0).owner.isdefault()
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.isdefault()
assert cfg_.option(pathread, 0).owner.isdefault()
assert cfg_.forcepermissive.option(pathread, 0).owner.isdefault()
elif not kwargs.get('propertyerror', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.get()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.get() == 'default'
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.get()")
assert cfg_.forcepermissive.option(pathread, 0).owner.get() == 'default'
#
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.isdefault()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.isdefault()
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.isdefault()")
assert cfg_.forcepermissive.option(pathread, 0).owner.isdefault()
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread, 0).owner.get()")
#
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).owner.isdefault()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread, 0).owner.isdefault()")
assert cfg.config(conf).unrestraint.option(pathread, 0).owner.get() == 'default'
assert cfg.config(conf).unrestraint.option(pathread, 0).owner.isdefault()
raises(PropertiesOptionError, "cfg_.option(pathread, 0).owner.isdefault()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread, 0).owner.isdefault()")
assert cfg_.unrestraint.option(pathread, 0).owner.get() == 'default'
assert cfg_.unrestraint.option(pathread, 0).owner.isdefault()
do(confread)
if confread != confwrite:
do(confwrite)
@ -450,40 +478,44 @@ def autocheck_get_value_permissive(cfg, mcfg, pathread, pathwrite, confread, con
def do(conf):
# get value after set value without permissive
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread, 0).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get() == empty_value
assert cfg_.option(pathread, 0).value.get() == empty_value
assert cfg_.forcepermissive.option(pathread, 0).value.get() == empty_value
if submulti_:
assert cfg.config(conf).option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
assert cfg_.option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
assert cfg_.forcepermissive.option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
else:
assert cfg.config(conf).option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
assert cfg_.option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
assert cfg_.forcepermissive.option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "assert cfg.config(conf).option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg.config(conf).option(pathread, 1).value.get()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get() == empty_value
raises(PropertiesOptionError, "assert cfg_.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg_.option(pathread, 1).value.get()")
assert cfg_.forcepermissive.option(pathread, 0).value.get() == empty_value
if submulti_:
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
assert cfg_.forcepermissive.option(pathread, 1).value.get() == SUBLIST_SECOND_VALUE[1]
else:
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
assert cfg_.forcepermissive.option(pathread, 1).value.get() == LIST_SECOND_VALUE[1]
else:
raises(PropertiesOptionError, "assert cfg.config(conf).option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg.config(conf).option(pathread, 1).value.get()")
raises(PropertiesOptionError, "assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get()")
raises(PropertiesOptionError, "assert cfg_.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg_.option(pathread, 1).value.get()")
raises(PropertiesOptionError, "assert cfg_.forcepermissive.option(pathread, 0).value.get()")
raises(PropertiesOptionError, "assert cfg_.forcepermissive.option(pathread, 1).value.get()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
with warnings.catch_warnings(record=True) as w:
assert cfg.config(conf).option(pathread).value.get() == first_value
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == first_value
assert cfg_.option(pathread).value.get() == first_value
assert cfg_.forcepermissive.option(pathread).value.get() == first_value
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).value.get()")
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == first_value
raises(PropertiesOptionError, "cfg_.option(pathread).value.get()")
assert cfg_.forcepermissive.option(pathread).value.get() == first_value
else:
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).value.get()")
raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).value.get()")
raises(PropertiesOptionError, "cfg_.option(pathread).value.get()")
raises(PropertiesOptionError, "cfg_.forcepermissive.option(pathread).value.get()")
with warnings.catch_warnings(record=True) as w:
do(confread)
if confread != confwrite:
@ -515,8 +547,12 @@ def autocheck_value_follower(cfg, mcfg, pathread, pathwrite, confread, confwrite
empty_value = kwargs['default']
def do(conf):
length = cfg.config(conf).option(pathread).value.len()
assert cfg.config(conf).forcepermissive.option(pathread).value.len() == length
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
length = cfg_.option(pathread).value.len()
assert cfg_.forcepermissive.option(pathread).value.len() == length
assert length == 2
do(confread)
if confread != confwrite:
@ -567,33 +603,41 @@ def autocheck_reset_value(cfg, mcfg, pathread, pathwrite, confread, confwrite, *
# reset value without permissive
with warnings.catch_warnings(record=True) as w:
if confwrite is not None:
cfg_ = cfg.config(confwrite)
else:
cfg_ = cfg
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
cfg.config(confwrite).option(pathwrite).value.reset()
cfg_.option(pathwrite).value.reset()
#else:
#FIXME raises(PropertiesOptionError, "cfg.config(confwrite).option(pathwrite).value.reset()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
cfg.config(confwrite).option(pathwrite, 0).value.reset()
cfg_.option(pathwrite, 0).value.reset()
#else:
#FIXME raises(PropertiesOptionError, "cfg.config(confwrite).option(pathwrite, 0).value.reset()")
# get value after reset value without permissive
def do(conf):
if confwrite is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread, 0).value.get() == empty_value
assert cfg.config(conf).option(pathread, 1).value.get() == second_value[1]
assert cfg_.option(pathread, 0).value.get() == empty_value
assert cfg_.option(pathread, 1).value.get() == second_value[1]
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread, 0).value.get()")
assert cfg.config(conf).forcepermissive.option(pathread, 0).value.get() == empty_value
assert cfg.config(conf).forcepermissive.option(pathread, 1).value.get() == second_value[1]
raises(PropertiesOptionError, "cfg_.option(pathread, 0).value.get()")
assert cfg_.forcepermissive.option(pathread, 0).value.get() == empty_value
assert cfg_.forcepermissive.option(pathread, 1).value.get() == second_value[1]
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(conf).option(pathread).value.get() == empty_value
assert cfg_.option(pathread).value.get() == empty_value
elif kwargs.get('permissive', False):
raises(PropertiesOptionError, "cfg.config(conf).option(pathread).value.get()")
assert cfg.config(conf).forcepermissive.option(pathread).value.get() == first_value
raises(PropertiesOptionError, "cfg_.option(pathread).value.get()")
assert cfg_.forcepermissive.option(pathread).value.get() == first_value
with warnings.catch_warnings(record=True) as w:
do(confread)
if confread != confwrite:
@ -606,16 +650,24 @@ def autocheck_append_value(cfg, mcfg, pathread, pathwrite, confread, confwrite,
submulti_ = cfg.unrestraint.option(pathread).option.issubmulti()
if not isleader:
return
if confread is not None:
cfg_ = cfg.config(confread)
else:
cfg_ = cfg
if confwrite is not None:
cfg2_ = cfg.config(confwrite)
else:
cfg2_ = cfg
with warnings.catch_warnings(record=True) as w:
if not kwargs.get('propertyerror', False):
leader_value = cfg.config(confread).forcepermissive.option(pathread).value.get()
leader_value = cfg_.forcepermissive.option(pathread).value.get()
len_value = len(leader_value)
leader_value.append(undefined)
assert len(cfg.config(confread).forcepermissive.option(pathread).value.get()) == len_value
assert len(cfg_.forcepermissive.option(pathread).value.get()) == len_value
with warnings.catch_warnings(record=True) as w:
cfg.forcepermissive.config(confwrite).option(pathread).value.set(leader_value)
new_leader_value = cfg.config(confread).forcepermissive.option(pathread).value.get()
cfg2_.forcepermissive.option(pathread).value.set(leader_value)
new_leader_value = cfg_.forcepermissive.option(pathread).value.get()
len_new = len(new_leader_value)
assert len_value + 1 == len_new
assert new_leader_value[-1] == kwargs['default_multi']
@ -625,16 +677,16 @@ def autocheck_append_value(cfg, mcfg, pathread, pathwrite, confread, confwrite,
else:
follower_path += '.third'
for idx in range(len_new):
assert cfg.config(confread).forcepermissive.option(follower_path, idx).value.get() == kwargs['default_multi']
assert cfg_.forcepermissive.option(follower_path, idx).value.get() == kwargs['default_multi']
#
if not submulti_:
value = 'value'
else:
value = ['value']
leader_value.append(value)
assert len(cfg.config(confread).forcepermissive.option(pathread).value.get()) == len(new_leader_value)
cfg.forcepermissive.config(confwrite).option(pathread).value.set(leader_value)
assert cfg.config(confread).forcepermissive.option(pathread).value.get()[-1] == value
assert len(cfg_.forcepermissive.option(pathread).value.get()) == len(new_leader_value)
cfg2_.forcepermissive.option(pathread).value.set(leader_value)
assert cfg_.forcepermissive.option(pathread).value.get()[-1] == value
@autocheck
@ -644,6 +696,14 @@ def autocheck_pop_value(cfg, mcfg, pathread, pathwrite, confread, confwrite, **k
if not isleader:
return
if confwrite is not None:
cfg_ = cfg.config(confwrite)
else:
cfg_ = cfg
if confread is not None:
cfg2_ = cfg.config(confread)
else:
cfg2_ = cfg
if not kwargs.get('propertyerror', False):
if not submulti_:
values = ['value1', 'value2', 'value3', 'value4']
@ -658,34 +718,34 @@ def autocheck_pop_value(cfg, mcfg, pathread, pathwrite, confread, confwrite, **k
else:
a_follower += '.third'
with warnings.catch_warnings(record=True) as w:
cfg.forcepermissive.config(confwrite).option(pathread).value.set(values)
cfg.forcepermissive.config(confwrite).option(a_follower, 1).value.set(follower_value)
cfg.config(confread).forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 0).owner.isdefault() is True
cfg.config(confread).forcepermissive.option(a_follower, 1).value.get() == follower_value
assert cfg.config(confread).forcepermissive.option(a_follower, 1).owner.isdefault() is False
cfg.config(confread).forcepermissive.option(a_follower, 2).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 2).owner.isdefault() is True
cfg.config(confread).forcepermissive.option(a_follower, 3).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 3).owner.isdefault() is True
cfg_.forcepermissive.option(pathread).value.set(values)
cfg_.forcepermissive.option(a_follower, 1).value.set(follower_value)
cfg2_.forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 0).owner.isdefault() is True
cfg2_.forcepermissive.option(a_follower, 1).value.get() == follower_value
assert cfg2_.forcepermissive.option(a_follower, 1).owner.isdefault() is False
cfg2_.forcepermissive.option(a_follower, 2).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 2).owner.isdefault() is True
cfg2_.forcepermissive.option(a_follower, 3).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 3).owner.isdefault() is True
#
cfg.forcepermissive.config(confwrite).option(pathread).value.pop(3)
cfg.config(confread).forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 0).owner.isdefault() is True
cfg.config(confread).forcepermissive.option(a_follower, 1).value.get() == follower_value
assert cfg.config(confread).forcepermissive.option(a_follower, 1).owner.isdefault() is False
cfg.config(confread).forcepermissive.option(a_follower, 2).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 2).owner.isdefault() is True
cfg_.forcepermissive.option(pathread).value.pop(3)
cfg2_.forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 0).owner.isdefault() is True
cfg2_.forcepermissive.option(a_follower, 1).value.get() == follower_value
assert cfg2_.forcepermissive.option(a_follower, 1).owner.isdefault() is False
cfg2_.forcepermissive.option(a_follower, 2).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 2).owner.isdefault() is True
#
cfg.forcepermissive.config(confwrite).option(pathread).value.pop(0)
cfg.config(confread).forcepermissive.option(a_follower, 0).value.get() == follower_value
assert cfg.config(confread).forcepermissive.option(a_follower, 0).owner.isdefault() is False
cfg.config(confread).forcepermissive.option(a_follower, 1).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 1).owner.isdefault() is True
cfg_.forcepermissive.option(pathread).value.pop(0)
cfg2_.forcepermissive.option(a_follower, 0).value.get() == follower_value
assert cfg2_.forcepermissive.option(a_follower, 0).owner.isdefault() is False
cfg2_.forcepermissive.option(a_follower, 1).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 1).owner.isdefault() is True
#
cfg.forcepermissive.config(confwrite).option(pathread).value.pop(0)
cfg.config(confread).forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg.config(confread).forcepermissive.option(a_follower, 0).owner.isdefault() is True
cfg_.forcepermissive.option(pathread).value.pop(0)
cfg2_.forcepermissive.option(a_follower, 0).value.get() == kwargs['default_multi']
assert cfg2_.forcepermissive.option(a_follower, 0).owner.isdefault() is True
@autocheck
@ -697,12 +757,20 @@ def autocheck_reset_value_permissive(cfg, mcfg, pathread, pathwrite, confread, c
with warnings.catch_warnings(record=True) as w:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
if not isfollower:
cfg.forcepermissive.config(confwrite).option(pathwrite).value.reset()
if confwrite is not None:
cfg_ = cfg.forcepermissive.config(confwrite)
else:
cfg_ = cfg.forcepermissive
cfg_.option(pathwrite).value.reset()
else:
cfg.forcepermissive.option(pathwrite, 1).value.reset()
elif kwargs.get('permissive', False):
if not isfollower:
cfg.forcepermissive.config(confwrite).option(pathwrite).value.reset()
if confwrite is not None:
cfg_ = cfg.forcepermissive.config(confwrite)
else:
cfg_ = cfg.forcepermissive
cfg_.option(pathwrite).value.reset()
else:
cfg.forcepermissive.option(pathwrite, 1).value.reset()
#FIXME else:
@ -723,13 +791,21 @@ def autocheck_display(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kwa
return
make_dict = kwargs['make_dict']
make_dict_value = kwargs['make_dict_value']
assert cfg.config(confread).value.dict() == make_dict
if confread is not None:
cfg_ = cfg.config(confread)
else:
cfg_ = cfg
if confwrite is not None:
cfg2_ = cfg.config(confwrite)
else:
cfg2_ = cfg
assert cfg_.value.dict() == make_dict
if confread != confwrite:
assert(cfg.config(confwrite).value.dict()) == make_dict
assert(cfg2_.value.dict()) == make_dict
_set_value(cfg, pathwrite, confwrite, **kwargs)
assert cfg.config(confread).value.dict() == make_dict_value
assert cfg_.value.dict() == make_dict_value
if confread != confwrite:
assert(cfg.config(confwrite).value.dict()) == make_dict_value
assert(cfg2_.value.dict()) == make_dict_value
@autocheck
@ -749,7 +825,11 @@ def autocheck_property(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kw
# set properties without permissive
for prop in properties:
cfg.unrestraint.config(confwrite).option(pathwrite).property.add(prop)
if confwrite is not None:
cfg_ = cfg.unrestraint.config(confwrite)
else:
cfg_ = cfg.unrestraint
cfg_.option(pathwrite).property.add(prop)
if confread == confwrite:
_check_properties(cfg, mcfg, pathread, confread, kwargs, properties, properties)
else:
@ -774,7 +854,11 @@ def autocheck_reset_property(cfg, mcfg, pathread, pathwrite, confread, confwrite
_property_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kwargs)
# reset properties without permissive
cfg.unrestraint.config(confwrite).option(pathwrite).property.reset()
if confwrite is not None:
cfg_ = cfg.unrestraint.config(confwrite)
else:
cfg_ = cfg.unrestraint
cfg_.option(pathwrite).property.reset()
if confread == confwrite:
_check_properties(cfg, mcfg, pathread, confread, kwargs, default_props, default_props)
@ -819,22 +903,28 @@ def autocheck_default_owner_with_value(cfg, mcfg, pathread, pathwrite, confread,
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
_set_value(cfg, pathwrite, confwrite, **kwargs)
if confwrite is not None:
cfg_ = cfg.config(confread)
cfg2_ = cfg.config(confwrite)
else:
cfg_ = cfg
cfg2_ = cfg
# test if is default owner without permissive
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(confwrite).option(pathread).owner.isdefault() is False
assert cfg_.option(pathread).owner.isdefault() is False
if confwrite != confread:
assert cfg.config(confread).option(pathread).owner.isdefault() is False
assert cfg2_.option(pathread).owner.isdefault() is False
#FIXME else:
# raises(PropertiesOptionError, "cfg.config(confwrite).option(pathread).owner.isdefault()")
# raises(PropertiesOptionError, "cfg.config(confread).option(pathread).owner.isdefault()")
else:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert cfg.config(confwrite).option(pathread, 0).owner.isdefault() is True
assert cfg.config(confwrite).option(pathread, 1).owner.isdefault() is False
assert cfg2_.option(pathread, 0).owner.isdefault() is True
assert cfg2_.option(pathread, 1).owner.isdefault() is False
if confwrite != confread:
assert cfg.config(confread).option(pathread, 0).owner.isdefault() is True
assert cfg.config(confread).option(pathread, 1).owner.isdefault() is False
assert cfg_.option(pathread, 0).owner.isdefault() is True
assert cfg_.option(pathread, 1).owner.isdefault() is False
#FIXME else:
# raises(PropertiesOptionError, "cfg.config(confwrite).option(pathread, 0).owner.isdefault()")
# raises(PropertiesOptionError, "cfg.config(confread).option(pathread, 0).owner.isdefault()")
@ -849,12 +939,16 @@ def autocheck_default_owner_with_value_permissive(cfg, mcfg, pathread, pathwrite
def do(conf):
# test if is default owner with permissive
if conf is not None:
cfg_ = cfg.config(conf)
else:
cfg_ = cfg
if not kwargs.get('propertyerror', False):
if not isfollower:
assert cfg.config(conf).forcepermissive.option(pathread).owner.isdefault() is False
assert cfg_.forcepermissive.option(pathread).owner.isdefault() is False
else:
assert cfg.config(conf).forcepermissive.option(pathread, 0).owner.isdefault() is True
assert cfg.config(conf).forcepermissive.option(pathread, 1).owner.isdefault() is False
assert cfg_.forcepermissive.option(pathread, 0).owner.isdefault() is True
assert cfg_.forcepermissive.option(pathread, 1).owner.isdefault() is False
#FIXME else:
# raises(PropertiesOptionError, "cfg.config(conf).forcepermissive.option(pathread).owner.isdefault()")
do(confwrite)
@ -865,11 +959,15 @@ def autocheck_default_owner_with_value_permissive(cfg, mcfg, pathread, pathwrite
@autocheck
def autocheck_set_owner_no_value(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kwargs):
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
if confwrite is not None:
cfg_ = cfg.forcepermissive.config(confwrite)
else:
cfg_ = cfg.forcepermissive
if not kwargs.get('propertyerror', False):
if not isfollower:
raises(ConfigError, "cfg.forcepermissive.config(confwrite).option(pathwrite).owner.set('new_user')")
raises(ConfigError, "cfg_.option(pathwrite).owner.set('new_user')")
else:
raises(ConfigError, "cfg.forcepermissive.option(pathwrite, 1).owner.set('new_user')")
raises(ConfigError, "cfg_.option(pathwrite, 1).owner.set('new_user')")
@autocheck
@ -878,13 +976,17 @@ def autocheck_set_owner(cfg, mcfg, pathread, pathwrite, confread, confwrite, **k
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
_set_value(cfg, pathwrite, confwrite, **kwargs)
if confwrite is not None:
cfg_ = cfg.config(confwrite)
else:
cfg_ = cfg
# set owner without permissive
if not isfollower:
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
cfg.config(confwrite).option(pathwrite).owner.set('new_user')
raises(ValueError, "cfg.config(confwrite).option(pathwrite).owner.set('default')")
raises(ValueError, "cfg.config(confwrite).option(pathwrite).owner.set('forced')")
cfg_.option(pathwrite).owner.set('new_user')
raises(ValueError, "cfg_.option(pathwrite).owner.set('default')")
raises(ValueError, "cfg_.option(pathwrite).owner.set('forced')")
#FIXME else:
# raises(PropertiesOptionError, "cfg.config(confwrite).option(pathwrite).owner.set('new_user')")
else:
@ -903,11 +1005,15 @@ def autocheck_set_owner_permissive(cfg, mcfg, pathread, pathwrite, confread, con
isfollower = cfg.unrestraint.option(pathread).option.isfollower()
_set_value(cfg, pathwrite, confwrite, **kwargs)
if confwrite is not None:
cfg_ = cfg.forcepermissive.config(confwrite)
else:
cfg_ = cfg.forcepermissive
# set owner with permissive
if not kwargs.get('propertyerror', False):
if not isfollower:
cfg.forcepermissive.config(confwrite).option(pathwrite).owner.set('new_user1')
cfg_.option(pathwrite).owner.set('new_user1')
else:
cfg.forcepermissive.option(pathwrite, 1).owner.set('new_user1')
#FIXME else:
@ -961,9 +1067,17 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
"""test permissive for hidden and disabled value
"""
# no permissive before
assert cfg.unrestraint.config(confwrite).option(pathread).permissive.get() == frozenset()
if confwrite is not None:
cfg_ = cfg.unrestraint.config(confwrite)
else:
cfg_ = cfg.unrestraint
if confread is not None:
cfg2_ = cfg.config(confread).unrestraint
else:
cfg2_ = cfg.unrestraint
assert cfg_.option(pathread).permissive.get() == frozenset()
if kwargs.get('permissive_od', False):
assert cfg.unrestraint.config(confwrite).option(pathread.rsplit('.', 1)[0]).permissive.get() == frozenset()
assert cfg_.option(pathread.rsplit('.', 1)[0]).permissive.get() == frozenset()
# cannot access to hidden value without forcepermissive
# and to disabled value (with forcepermissive too)
@ -972,17 +1086,17 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
_autocheck_default_value(cfg, pathread, confread, **kwargs)
# set permissive
cfg.unrestraint.config(confwrite).option(pathwrite).permissive.set(frozenset(['disabled']))
cfg_.option(pathwrite).permissive.set(frozenset(['disabled']))
callback = kwargs['callback']
if callback:
if pathread.endswith('val1') or pathread.endswith('val2'):
call_path = pathread[:-4] + 'call' + pathread[-4:]
else:
call_path = pathread + 'call'
cfg.unrestraint.config(confwrite).option(call_path).permissive.set(frozenset(['disabled']))
cfg_.option(call_path).permissive.set(frozenset(['disabled']))
# have permissive?
assert cfg.unrestraint.config(confwrite).option(pathread).permissive.get() == frozenset(['disabled'])
assert cfg_.option(pathread).permissive.get() == frozenset(['disabled'])
#if confwrite != confread:
# assert cfg.config(confread).unrestraint.option(pathread).permissive.get() == frozenset(['disabled'])
@ -991,9 +1105,9 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
ckwargs['propertyerror'] = False
_autocheck_default_value(cfg, pathread, confwrite, **ckwargs)
cfg.config(confread).unrestraint.option(pathwrite).permissive.set(frozenset(['disabled', 'hidden']))
cfg2_.option(pathwrite).permissive.set(frozenset(['disabled', 'hidden']))
if kwargs['callback']:
cfg.config(confread).unrestraint.option(call_path).permissive.set(frozenset(['disabled', 'hidden']))
cfg2_.option(call_path).permissive.set(frozenset(['disabled', 'hidden']))
# can access to all value except when optiondescript have hidden
if not ckwargs.get('permissive_od', False):
@ -1002,7 +1116,7 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
if ckwargs.get('permissive_od', False):
# set permissive to OptionDescription
cfg.config(confread).unrestraint.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset(['disabled',
cfg2_.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset(['disabled',
'hidden']))
ckwargs['permissive'] = False
_autocheck_default_value(cfg, pathread, confread, **ckwargs)
@ -1010,23 +1124,23 @@ def autocheck_permissive(cfg, mcfg, pathread, pathwrite, confread, confwrite, **
# _autocheck_default_value(cfg, pathread, confwrite, **ckwargs)
# only hidden
cfg.config(confread).unrestraint.option(pathwrite).permissive.set(frozenset(['hidden']))
cfg2_.option(pathwrite).permissive.set(frozenset(['hidden']))
if callback:
cfg.config(confread).unrestraint.option(call_path).permissive.set(frozenset(['hidden']))
cfg2_.option(call_path).permissive.set(frozenset(['hidden']))
if ckwargs.get('permissive_od', False):
_autocheck_default_value(cfg, pathread, confread, **ckwargs)
cfg.config(confread).unrestraint.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset(['hidden']))
cfg2_.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset(['hidden']))
ckwargs = copy(kwargs)
ckwargs['permissive'] = False
_autocheck_default_value(cfg, pathread, confread, **ckwargs)
# no permissive
cfg.config(confread).unrestraint.option(pathwrite).permissive.set(frozenset())
cfg2_.option(pathwrite).permissive.set(frozenset())
if callback:
cfg.config(confread).unrestraint.option(call_path).permissive.set(frozenset())
cfg2_.option(call_path).permissive.set(frozenset())
if ckwargs.get('permissive_od', False):
_autocheck_default_value(cfg, pathread, confread, **ckwargs)
cfg.config(confread).unrestraint.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset())
cfg2_.option(pathwrite.rsplit('.', 1)[0]).permissive.set(frozenset())
_autocheck_default_value(cfg, pathread, confread, **kwargs)
@ -1060,17 +1174,21 @@ def autocheck_find(cfg, mcfg, pathread, pathwrite, confread, confwrite, **kwargs
option = _getoption(cfg.unrestraint.option(pathread))
def do(conf):
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert option == _getoption(cfg.config(conf).option.find(name, first=True))
assert option == _getoption(cfg.config(conf).forcepermissive.option.find(name, first=True))
elif kwargs.get('permissive', False):
raises(AttributeError, "cfg.config(conf).option.find(name, first=True)")
assert option == _getoption(cfg.config(conf).forcepermissive.option.find(name, first=True))
if conf is not None:
cfg_ = cfg.config(conf)
else:
raises(AttributeError, "cfg.config(conf).option.find(name, first=True)")
raises(AttributeError, "cfg.config(conf).forcepermissive.option.find(name, first=True)")
assert option == _getoption(cfg.config(conf).unrestraint.option.find(name, first=True))
assert [option] == _getoptions(cfg.config(conf).unrestraint.option.find(name))
cfg_ = cfg
if not kwargs.get('permissive', False) and not kwargs.get('propertyerror', False):
assert option == _getoption(cfg_.option.find(name, first=True))
assert option == _getoption(cfg_.forcepermissive.option.find(name, first=True))
elif kwargs.get('permissive', False):
raises(AttributeError, "cfg_.option.find(name, first=True)")
assert option == _getoption(cfg_.forcepermissive.option.find(name, first=True))
else:
raises(AttributeError, "cfg_.option.find(name, first=True)")
raises(AttributeError, "cfg_.forcepermissive.option.find(name, first=True)")
assert option == _getoption(cfg_.unrestraint.option.find(name, first=True))
assert [option] == _getoptions(cfg_.unrestraint.option.find(name))
do(confread)
if confread != confwrite:
do(confwrite)

View File

@ -264,6 +264,23 @@ def test_config_multi():
assert config.option('test3').value.get() == [2, 1]
def test_prefix_error():
i1 = IntOption('test1', '')
od = OptionDescription('test', '', [i1])
config = Config(od)
config.property.read_write()
config.option('test1').value.set(1)
try:
config.option('test1').value.set('yes')
except Exception as err:
assert str(err) == '"yes" is an invalid integer for "test1"'
try:
config.option('test1').value.set('yes')
except Exception as err:
err.prefix = ''
assert str(err) == 'invalid value'
def test_no_validation():
i1 = IntOption('test1', '')
od = OptionDescription('test', '', [i1])

View File

@ -4,8 +4,7 @@ do_autopath()
import warnings, sys
from py.test import raises
from tiramisu import Config
from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription
from tiramisu import Config, DomainnameOption, EmailOption, URLOption, OptionDescription
from tiramisu.error import ValueWarning
from tiramisu.i18n import _
from tiramisu.storage import list_sessions
@ -19,9 +18,11 @@ def test_domainname():
d = DomainnameOption('d', '')
f = DomainnameOption('f', '', allow_without_dot=True)
g = DomainnameOption('g', '', allow_ip=True)
od = OptionDescription('a', '', [d, f, g])
h = DomainnameOption('h', '', allow_ip=True, cidr=True)
od = OptionDescription('a', '', [d, f, g, h])
cfg = Config(od)
cfg.property.read_write()
#
cfg.option('d').value.set('toto.com')
raises(ValueError, "cfg.option('d').value.set('toto')")
cfg.option('d').value.set('toto3.com')
@ -40,9 +41,17 @@ def test_domainname():
cfg.option('f').value.set('d.t')
#
raises(ValueError, "cfg.option('f').value.set('192.168.1.1')")
raises(ValueError, "cfg.option('f').value.set('192.168.1.0/24')")
#
cfg.option('g').value.set('toto.com')
cfg.option('g').value.set('192.168.1.0')
cfg.option('g').value.set('192.168.1.29')
raises(ValueError, "cfg.option('g').value.set('192.168.1.0/24')")
#
cfg.option('h').value.set('toto.com')
raises(ValueError, "cfg.option('h').value.set('192.168.1.0')")
raises(ValueError, "cfg.option('h').value.set('192.168.1.29')")
cfg.option('h').value.set('192.168.1.0/24')
def test_domainname_upper():

View File

@ -417,6 +417,7 @@ def test_mandatory_leader():
api = Config(descr)
api.property.read_only()
raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "api.value.dict()")
def test_mandatory_warnings_leader():

View File

@ -63,6 +63,12 @@ def test_unknown_config():
raises(ConfigError, "meta.config('unknown')")
def test_error_metaconfig():
od2 = make_description()
conf1 = Config(od2, session_id='conf1')
raises(TypeError, "MetaConfig([GroupConfig([conf1])], session_id='meta')")
def test_path():
meta = make_metaconfig()
assert meta.config.path() == 'meta'

View File

@ -98,6 +98,16 @@ def test_option_isoptiondescription():
assert not cfg.option('od.test').option.isoptiondescription()
def test_option_double():
i = IntOption('test', '')
od = OptionDescription('od1', '', [i])
od = OptionDescription('od2', '', [od])
od = OptionDescription('od3', '', [od])
cfg = Config(od)
assert cfg.option('od2.od1.test').value.get() is None
assert cfg.option('od2').option('od1').option('test').value.get() is None
def test_option_multi():
IntOption('test', '', multi=True)
IntOption('test', '', multi=True, default_multi=1)
@ -227,3 +237,8 @@ def test_intoption():
def test_get_display_type():
i1 = IntOption('test1', 'description', min_number=3)
assert i1.get_display_type() == 'integer'
def test_option_not_in_config():
i1 = IntOption('test1', 'description', min_number=3)
raises(AttributeError, "i1.impl_getpath()")

View File

@ -8,7 +8,7 @@ from tiramisu.config import KernelConfig
from tiramisu.setting import groups, owners
from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription, SymLinkOption, IPOption, NetmaskOption, Leadership, \
undefined, Params, ParamOption, ParamValue, ParamContext
undefined, Params, ParamOption, ParamValue, ParamContext, calc_value
from tiramisu.api import TIRAMISU_VERSION
from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError
from tiramisu.i18n import _
@ -1194,3 +1194,95 @@ def test_callback_raise():
api.option('od2.opt2').value.get()
except ConfigError as err:
assert '"Option 2"' in str(err)
def test_calc_value_simple():
val1 = StrOption('val1', '', 'val1')
val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1)))
od = OptionDescription('root', '', [val1, val2])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1'}
def test_calc_value_multi():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "", 'val2')
val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True)))
od = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': ['val1', 'val2']}
def test_calc_value_disabled():
val1 = StrOption('val1', '', 'val1')
val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True), default=ParamValue('default_value')))
od = OptionDescription('root', '', [val1, val2])
cfg = Config(od)
cfg.property.read_write()
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1'}
cfg.option('val1').property.add('disabled')
assert cfg.value.dict() == {'val2': 'default_value'}
def test_calc_value_condition():
boolean = BoolOption('boolean', '', True)
val1 = StrOption('val1', '', 'val1')
val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True),
default=ParamValue('default_value'),
condition=ParamOption(boolean),
expected=ParamValue(True)))
od = OptionDescription('root', '', [boolean, val1, val2])
cfg = Config(od)
cfg.property.read_write()
assert cfg.value.dict() == {'boolean': True, 'val1': 'val1', 'val2': 'val1'}
cfg.option('boolean').value.set(False)
assert cfg.value.dict() == {'boolean': False, 'val1': 'val1', 'val2': 'default_value'}
def test_calc_value_allow_none():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "")
val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), allow_none=ParamValue(True)))
od = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 'val1', 'val2': None, 'val3': ['val1', None]}
def test_calc_value_remove_duplicate():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "", 'val1')
val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), remove_duplicate_value=ParamValue(True)))
od = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1', 'val3': ['val1']}
def test_calc_value_join():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "", 'val2')
val3 = StrOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), join=ParamValue('.')))
od = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': 'val1.val2'}
def test_calc_value_min():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "", 'val2')
val3 = StrOption('val3', "", 'val3')
val4 = StrOption('val4', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2), ParamOption(val3, True)), join=ParamValue('.'), min_args_len=ParamValue(3)))
od = OptionDescription('root', '', [val1, val2, val3, val4])
cfg = Config(od)
cfg.property.read_write()
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'}
cfg.option('val3').property.add('disabled')
assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val4': ''}
def test_calc_value_add():
val1 = IntOption('val1', "", 1)
val2 = IntOption('val2', "", 2)
val3 = IntOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), operator=ParamValue('add')))
od = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od)
assert cfg.value.dict() == {'val1': 1, 'val2': 2, 'val3': 3}

View File

@ -88,6 +88,24 @@ def test_consistency_warnings_only_more_option():
assert len(w) == 1
def test_consistency_error_prefix():
a = IntOption('a', '')
b = IntOption('b', '')
od = OptionDescription('od', '', [a, b])
a.impl_add_consistency('not_equal', b)
api = Config(od)
api.option('a').value.set(1)
try:
api.option('b').value.set(1)
except Exception as err:
assert str(err) == '"1" is an invalid integer for "b", must be different from the value of "a"'
try:
api.option('b').value.set(1)
except Exception as err:
err.prefix = ''
assert str(err) == 'must be different from the value of "a"'
def test_consistency_warnings_only_option():
a = IntOption('a', '')
b = IntOption('b', '', warnings_only=True)

View File

@ -8,7 +8,7 @@ from tiramisu.setting import groups
from tiramisu import setting
setting.expires_time = 1
from tiramisu import IPOption, OptionDescription, BoolOption, IntOption, StrOption, \
Leadership, Config
Leadership, Config, calc_value, Params, ParamOption
from tiramisu.error import PropertiesOptionError, RequirementError
from py.test import raises
from tiramisu.storage import list_sessions, delete_session
@ -65,6 +65,27 @@ def test_requires():
api.option('ip_address_service').value.get()
def test_requires_callback():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '',
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(a)), 'expected': False, 'action': 'disabled'}])
od = OptionDescription('service', '', [a, b])
api = Config(od)
api.property.read_write()
assert not api.option('activate_service').option.requires()
assert api.option('ip_address_service').option.requires()
api.option('ip_address_service').value.get()
api.option('activate_service').value.set(False)
props = []
try:
api.option('ip_address_service').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
api.option('activate_service').value.set(True)
api.option('ip_address_service').value.get()
def test_requires_inverse():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '',
@ -179,6 +200,44 @@ def test_requires_same_action():
assert frozenset(props) == frozenset(['disabled'])
def test_requires_same_action_callback():
activate_service = BoolOption('activate_service', '', True)
activate_service_web = BoolOption('activate_service_web', '', True,
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(activate_service)), 'expected': False,
'action': 'new'}])
ip_address_service_web = IPOption('ip_address_service_web', '',
requires=[{'option': activate_service_web, 'expected': False,
'action': 'disabled', 'inverse': False,
'transitive': True, 'same_action': False}])
od1 = OptionDescription('service', '', [activate_service, activate_service_web, ip_address_service_web])
api = Config(od1)
api.property.read_write()
api.property.add('new')
api.option('activate_service').value.get()
api.option('activate_service_web').value.get()
api.option('ip_address_service_web').value.get()
api.option('activate_service').value.set(False)
#
props = []
try:
api.option('activate_service_web').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['new'])
#
props = []
try:
api.option('ip_address_service_web').value.get()
except PropertiesOptionError as err:
props = err.proptype
submsg = '"disabled" (' + _('the calculated value is {0}').format('"False"') + ')'
assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', 'property', submsg))
#access to cache
assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', 'property', submsg))
assert frozenset(props) == frozenset(['disabled'])
def test_multiple_requires():
a = StrOption('activate_service', '')
b = IPOption('ip_address_service', '',
@ -326,6 +385,36 @@ def test_requires_transitive():
assert frozenset(props) == frozenset(['disabled'])
def test_requires_transitive_callback():
a = BoolOption('activate_service', '', True)
b = BoolOption('activate_service_web', '', True,
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(a)), 'expected': False, 'action': 'disabled'}])
d = IPOption('ip_address_service_web', '',
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(b)), 'expected': False, 'action': 'disabled'}])
od = OptionDescription('service', '', [a, b, d])
api = Config(od)
api.property.read_write()
api.option('activate_service').value.get()
api.option('activate_service_web').value.get()
api.option('ip_address_service_web').value.get()
api.option('activate_service').value.set(False)
#
props = []
try:
api.option('activate_service_web').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
#
props = []
try:
api.option('ip_address_service_web').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
def test_requires_transitive_unrestraint():
a = BoolOption('activate_service', '', True)
b = BoolOption('activate_service_web', '', True,
@ -589,6 +678,46 @@ def test_requires_multi_disabled():
assert frozenset(props) == frozenset(['disabled'])
def test_requires_multi_disabled_callback():
a = BoolOption('activate_service', '')
b = IntOption('num_service', '')
c = IPOption('ip_address_service', '',
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(a)), 'expected': True, 'action': 'disabled'},
{'callback': calc_value, 'callback_params': Params(ParamOption(b)), 'expected': 1, 'action': 'disabled'}])
od = OptionDescription('service', '', [a, b, c])
api = Config(od)
api.property.read_write()
api.option('ip_address_service').value.get()
api.option('activate_service').value.set(True)
props = []
try:
api.option('ip_address_service').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
api.option('activate_service').value.set(False)
api.option('ip_address_service').value.get()
api.option('num_service').value.set(1)
props = []
try:
api.option('ip_address_service').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
api.option('activate_service').value.set(True)
props = []
try:
api.option('ip_address_service').value.get()
except PropertiesOptionError as err:
props = err.proptype
assert frozenset(props) == frozenset(['disabled'])
def test_requires_multi_disabled_new_format():
a = BoolOption('activate_service', '')
b = IntOption('num_service', '')
@ -1006,6 +1135,56 @@ def test_leadership_requires():
del ret['ip_admin_eth0.netmask_admin_eth0']
def test_leadership_requires_callback():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True,
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(ip_admin_eth0)), 'expected': '192.168.1.1', 'action': 'disabled'}])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
maconfig = OptionDescription('toto', '', [interface1])
api = Config(maconfig)
api.property.read_write()
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2']
#
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()")
#
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.2'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() is None
api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('255.255.255.255')
assert api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == '255.255.255.255'
assert api.value.dict() == {'ip_admin_eth0.ip_admin_eth0': ['192.168.1.2', '192.168.1.2'],
'ip_admin_eth0.netmask_admin_eth0': [None, '255.255.255.255']}
#
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()")
ret = api.value.dict()
assert set(ret.keys()) == set(['ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert ret['ip_admin_eth0.ip_admin_eth0'] == ['192.168.1.2', '192.168.1.1']
assert len(ret['ip_admin_eth0.netmask_admin_eth0']) == 2
assert ret['ip_admin_eth0.netmask_admin_eth0'][0] is None
assert isinstance(ret['ip_admin_eth0.netmask_admin_eth0'][1], PropertiesOptionError)
del ret['ip_admin_eth0.netmask_admin_eth0'][1]
del ret['ip_admin_eth0.netmask_admin_eth0'][0]
del ret['ip_admin_eth0.netmask_admin_eth0']
#
api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('255.255.255.255')
ret = api.value.dict()
assert set(ret.keys()) == set(['ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert ret['ip_admin_eth0.ip_admin_eth0'] == ['192.168.1.2', '192.168.1.1']
assert len(ret['ip_admin_eth0.netmask_admin_eth0']) == 2
assert ret['ip_admin_eth0.netmask_admin_eth0'][0] == '255.255.255.255'
assert isinstance(ret['ip_admin_eth0.netmask_admin_eth0'][1], PropertiesOptionError)
del ret['ip_admin_eth0.netmask_admin_eth0'][1]
del ret['ip_admin_eth0.netmask_admin_eth0'][0]
del ret['ip_admin_eth0.netmask_admin_eth0']
def test_leadership_requires_both():
ip_admin = StrOption('ip_admin_eth0', "ip réseau autorisé")
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True,
@ -1037,7 +1216,7 @@ def test_leadership_requires_properties():
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True,
requires=[{'option': ip_admin_eth0, 'expected': '192.168.1.1', 'action': 'disabled'}])
Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], properties=('hidden',),
requires=[{'option': ip_admin, 'expected': '192.168.1.1', 'action': 'disabled'}])
requires=[{'option': ip_admin, 'expected': '192.168.1.1', 'action': 'disabled'}])
def test_leadership_requires_leader():
@ -1068,6 +1247,34 @@ def test_leadership_requires_leader():
assert api.value.dict() == {'activate': False}
def test_leadership_requires_leader_callback():
activate = BoolOption('activate', "Activer l'accès au réseau", True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True,
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(activate)), 'expected': False, 'action': 'disabled'}])
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
maconfig = OptionDescription('toto', '', [activate, interface1])
api = Config(maconfig)
api.property.read_write()
#
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2']
#
api.option('activate').value.set(False)
raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
#
api.option('activate').value.set(True)
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
#
api.option('activate').value.set(False)
raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
assert api.value.dict() == {'activate': False}
def test_leadership_requires_leadership():
activate = BoolOption('activate', "Activer l'accès au réseau", True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
@ -1096,6 +1303,34 @@ def test_leadership_requires_leadership():
assert api.value.dict() == {'activate': False}
def test_leadership_requires_leadership_callback():
activate = BoolOption('activate', "Activer l'accès au réseau", True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0],
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(activate)), 'expected': False, 'action': 'disabled'}])
maconfig = OptionDescription('toto', '', [activate, interface1])
api = Config(maconfig)
api.property.read_write()
#
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2']
#
api.option('activate').value.set(False)
raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
#
api.option('activate').value.set(True)
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
#
api.option('activate').value.set(False)
raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
assert api.value.dict() == {'activate': False}
def test_leadership_requires_no_leader():
activate = BoolOption('activate', "Activer l'accès au réseau", True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
@ -1123,3 +1358,460 @@ def test_leadership_requires_no_leader():
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()")
assert api.value.dict() == {'ip_admin_eth0.ip_admin_eth0': ['192.168.1.2', '192.168.1.1'], 'activate': False}
def test_leadership_requires_no_leader_callback():
activate = BoolOption('activate', "Activer l'accès au réseau", True)
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True,
requires=[{'callback': calc_value, 'callback_params': Params(ParamOption(activate)), 'expected': False, 'action': 'disabled'}])
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
maconfig = OptionDescription('toto', '', [activate, interface1])
api = Config(maconfig)
api.property.read_write()
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2'])
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2']
api.option('activate').value.set(False)
api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1'])
assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2', '192.168.1.1']
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()")
api.option('activate').value.set(True)
assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() is None
api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('255.255.255.255')
assert api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == '255.255.255.255'
api.option('activate').value.set(False)
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()")
raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()")
dico = api.value.dict()
assert set(dico.keys()) == {'activate', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'}
dico['ip_admin_eth0.ip_admin_eth0'] == ['192.168.1.2', '192.168.1.1']
dico['activate'] == False
del dico['ip_admin_eth0.netmask_admin_eth0'][1]
del dico['ip_admin_eth0.netmask_admin_eth0'][0]
def test_leadership_requires_complet():
optiontoto = StrOption('unicodetoto', "Unicode leader")
option = StrOption('unicode', "Unicode leader", multi=True)
option1 = StrOption('unicode1', "Unicode follower 1", multi=True)
option2 = StrOption('unicode2', "Values 'test' must show 'Unicode follower 3'", multi=True)
option3 = StrOption('unicode3', "Unicode follower 3", requires=[{'option': option,
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option4 = StrOption('unicode4', "Unicode follower 4", requires=[{'option': option2,
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option5 = StrOption('unicode5', "Unicode follower 5", requires=[{'option': optiontoto,
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option6 = StrOption('unicode6', "Unicode follower 6", requires=[{'option': optiontoto,
'expected': u'test',
'action': 'hidden',
'inverse': True},
{'option': option2,
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option7 = StrOption('unicode7', "Unicode follower 7", requires=[{'option': option2,
'expected': u'test',
'action': 'hidden',
'inverse': True},
{'option': optiontoto,
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
descr1 = Leadership("unicode", "Common configuration 1",
[option, option1, option2, option3, option4, option5, option6, option7])
descr = OptionDescription("options", "Common configuration 2", [descr1, optiontoto])
descr = OptionDescription("unicode1_leadership_requires", "Leader followers with Unicode follower 3 hidden when Unicode follower 2 is test", [descr])
config = Config(descr)
config.property.read_write()
config.option('options.unicode.unicode').value.set(['test', 'trah'])
config.option('options.unicode.unicode2', 0).value.set('test')
dico = config.value.dict()
assert dico.keys() == set(['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto'])
assert dico['options.unicode.unicode'] == ['test', 'trah']
assert dico['options.unicode.unicode1'] == [None, None]
assert dico['options.unicode.unicode2'] == ['test', None]
assert dico['options.unicode.unicode3'][0] is None
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert dico['options.unicode.unicode4'][0] is None
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert dico['options.unicodetoto'] is None
del dico['options.unicode.unicode3'][1]
del dico['options.unicode.unicode3']
del dico['options.unicode.unicode4'][1]
del dico['options.unicode.unicode4']
#
config.option('options.unicodetoto').value.set('test')
dico = config.value.dict()
assert dico.keys() == set(['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicode.unicode5', 'options.unicode.unicode6', 'options.unicode.unicode7', 'options.unicodetoto'])
assert dico['options.unicode.unicode'] == ['test', 'trah']
assert dico['options.unicode.unicode1'] == [None, None]
assert dico['options.unicode.unicode2'] == ['test', None]
assert dico['options.unicode.unicode3'][0] is None
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert dico['options.unicode.unicode4'][0] is None
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert dico['options.unicode.unicode5'] == [None, None]
assert dico['options.unicode.unicode6'][0] is None
assert isinstance(dico['options.unicode.unicode6'][1], PropertiesOptionError)
assert dico['options.unicode.unicode7'][0] is None
assert isinstance(dico['options.unicode.unicode7'][1], PropertiesOptionError)
assert dico['options.unicodetoto'] == 'test'
del dico['options.unicode.unicode3'][1]
del dico['options.unicode.unicode3']
del dico['options.unicode.unicode4'][1]
del dico['options.unicode.unicode4']
del dico['options.unicode.unicode6'][1]
del dico['options.unicode.unicode6']
del dico['options.unicode.unicode7'][1]
del dico['options.unicode.unicode7']
def test_leadership_requires_complet_callback():
optiontoto = StrOption('unicodetoto', "Unicode leader")
option = StrOption('unicode', "Unicode leader", multi=True)
option1 = StrOption('unicode1', "Unicode follower 1", multi=True)
option2 = StrOption('unicode2', "Values 'test' must show 'Unicode follower 3'", multi=True)
option3 = StrOption('unicode3', "Unicode follower 3", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(option)),
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option4 = StrOption('unicode4', "Unicode follower 4", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(option2)),
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option5 = StrOption('unicode5', "Unicode follower 5", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(optiontoto)),
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option6 = StrOption('unicode6', "Unicode follower 6", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(optiontoto)),
'expected': u'test',
'action': 'hidden',
'inverse': True},
{'callback': calc_value,
'callback_params': Params(ParamOption(option2)),
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
option7 = StrOption('unicode7', "Unicode follower 7", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(option2)),
'expected': u'test',
'action': 'hidden',
'inverse': True},
{'callback': calc_value,
'callback_params': Params(ParamOption(optiontoto)),
'expected': u'test',
'action': 'hidden',
'inverse': True}],
multi=True)
descr1 = Leadership("unicode", "Common configuration 1",
[option, option1, option2, option3, option4, option5, option6, option7])
descr = OptionDescription("options", "Common configuration 2", [descr1, optiontoto])
descr = OptionDescription("unicode1_leadership_requires", "Leader followers with Unicode follower 3 hidden when Unicode follower 2 is test", [descr])
config = Config(descr)
config.property.read_write()
config.option('options.unicode.unicode').value.set(['test', 'trah'])
config.option('options.unicode.unicode2', 0).value.set('test')
dico = config.value.dict()
assert dico.keys() == set(['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicode.unicode5', 'options.unicode.unicode6', 'options.unicode.unicode7', 'options.unicodetoto'])
assert dico['options.unicode.unicode'] == ['test', 'trah']
assert dico['options.unicode.unicode1'] == [None, None]
assert dico['options.unicode.unicode2'] == ['test', None]
assert dico['options.unicode.unicode3'][0] is None
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert dico['options.unicode.unicode4'][0] is None
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert dico['options.unicodetoto'] is None
del dico['options.unicode.unicode3'][1]
del dico['options.unicode.unicode3']
del dico['options.unicode.unicode4'][1]
del dico['options.unicode.unicode4']
del dico['options.unicode.unicode5'][0]
del dico['options.unicode.unicode5'][0]
del dico['options.unicode.unicode6'][0]
del dico['options.unicode.unicode6'][0]
del dico['options.unicode.unicode7'][0]
del dico['options.unicode.unicode7'][0]
#
config.option('options.unicodetoto').value.set('test')
dico = config.value.dict()
assert dico.keys() == set(['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicode.unicode5', 'options.unicode.unicode6', 'options.unicode.unicode7', 'options.unicodetoto'])
assert dico['options.unicode.unicode'] == ['test', 'trah']
assert dico['options.unicode.unicode1'] == [None, None]
assert dico['options.unicode.unicode2'] == ['test', None]
assert dico['options.unicode.unicode3'][0] is None
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert dico['options.unicode.unicode4'][0] is None
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert dico['options.unicode.unicode5'] == [None, None]
assert dico['options.unicode.unicode6'][0] is None
assert isinstance(dico['options.unicode.unicode6'][1], PropertiesOptionError)
assert dico['options.unicode.unicode7'][0] is None
assert isinstance(dico['options.unicode.unicode7'][1], PropertiesOptionError)
assert dico['options.unicodetoto'] == 'test'
del dico['options.unicode.unicode3'][1]
del dico['options.unicode.unicode3']
del dico['options.unicode.unicode4'][1]
del dico['options.unicode.unicode4']
del dico['options.unicode.unicode6'][1]
del dico['options.unicode.unicode6']
del dico['options.unicode.unicode7'][1]
del dico['options.unicode.unicode7']
def test_leadership_requires_transitive():
optiontoto = StrOption('unicodetoto', "Unicode leader")
option = StrOption('unicode', "Unicode leader", multi=True)
option1 = StrOption('unicode1', "Unicode follower 1", multi=True)
option2 = StrOption('unicode2', "Unicode follower 2", requires=[{'option': optiontoto,
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
option3 = StrOption('unicode3', "Unicode follower 3", requires=[{'option': option2,
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
option4 = StrOption('unicode4', "Unicode follower 4", requires=[{'option': option3,
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
descr1 = Leadership("unicode", "Common configuration 1",
[option, option1, option2, option3, option4])
descr = OptionDescription("options", "Common configuration 2", [descr1, optiontoto])
descr = OptionDescription("unicode1", "", [descr])
config = Config(descr)
config.property.read_write()
assert config.value.dict() == {'options.unicode.unicode': [], 'options.unicode.unicode1': [], 'options.unicode.unicode3': [], 'options.unicode.unicode4': [], 'options.unicodetoto': None}
#
config.option('options.unicodetoto').value.set('test')
assert config.value.dict() == {'options.unicode.unicode': [], 'options.unicode.unicode1': [], 'options.unicode.unicode2': [], 'options.unicode.unicode3': [], 'options.unicode.unicode4': [], 'options.unicodetoto': 'test'}
#
config.option('options.unicode.unicode').value.set(['a', 'b', 'c'])
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, None, None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('test')
config.option('options.unicode.unicode3', 1).value.set('test')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, 'test', None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert dico['options.unicode.unicode3'][1] == 'test'
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert dico['options.unicode.unicode4'][1] == None
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('rah')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, 'rah', None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('test')
config.option('options.unicodetoto').value.set('rah')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'rah'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
def test_leadership_requires_transitive_callback():
optiontoto = StrOption('unicodetoto', "Unicode leader")
option = StrOption('unicode', "Unicode leader", multi=True)
option1 = StrOption('unicode1', "Unicode follower 1", multi=True)
option2 = StrOption('unicode2', "Unicode follower 2", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(optiontoto)),
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
option3 = StrOption('unicode3', "Unicode follower 3", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(option2)),
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
option4 = StrOption('unicode4', "Unicode follower 4", requires=[{'callback': calc_value,
'callback_params': Params(ParamOption(option3)),
'expected': u'test',
'action': 'disabled',
'transitive': True,
'inverse': True}],
multi=True)
descr1 = Leadership("unicode", "Common configuration 1",
[option, option1, option2, option3, option4])
descr = OptionDescription("options", "Common configuration 2", [descr1, optiontoto])
descr = OptionDescription("unicode1", "", [descr])
config = Config(descr)
config.property.read_write()
assert config.value.dict() == {'options.unicode.unicode': [], 'options.unicode.unicode1': [], 'options.unicode.unicode2': [], 'options.unicode.unicode3': [], 'options.unicode.unicode4': [], 'options.unicodetoto': None}
#
config.option('options.unicodetoto').value.set('test')
assert config.value.dict() == {'options.unicode.unicode': [], 'options.unicode.unicode1': [], 'options.unicode.unicode2': [], 'options.unicode.unicode3': [], 'options.unicode.unicode4': [], 'options.unicodetoto': 'test'}
#
config.option('options.unicode.unicode').value.set(['a', 'b', 'c'])
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, None, None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('test')
config.option('options.unicode.unicode3', 1).value.set('test')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, 'test', None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert dico['options.unicode.unicode3'][1] == 'test'
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert dico['options.unicode.unicode4'][1] == None
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('rah')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'test'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert dico['options.unicode.unicode2'] == [None, 'rah', None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])
#
config.option('options.unicode.unicode2', 1).value.set('test')
config.option('options.unicodetoto').value.set('rah')
dico = config.value.dict()
assert list(dico.keys()) == ['options.unicode.unicode', 'options.unicode.unicode1', 'options.unicode.unicode2', 'options.unicode.unicode3', 'options.unicode.unicode4', 'options.unicodetoto']
assert dico['options.unicodetoto'] == 'rah'
assert dico['options.unicode.unicode'] == ['a', 'b', 'c']
assert dico['options.unicode.unicode1'] == [None, None, None]
assert isinstance(dico['options.unicode.unicode3'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode3'][2], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][0], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][1], PropertiesOptionError)
assert isinstance(dico['options.unicode.unicode4'][2], PropertiesOptionError)
del (dico['options.unicode.unicode2'][2])
del (dico['options.unicode.unicode2'][1])
del (dico['options.unicode.unicode2'][0])
del (dico['options.unicode.unicode3'][2])
del (dico['options.unicode.unicode3'][1])
del (dico['options.unicode.unicode3'][0])
del (dico['options.unicode.unicode4'][2])
del (dico['options.unicode.unicode4'][1])
del (dico['options.unicode.unicode4'][0])

View File

@ -13,7 +13,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .function import Params, ParamOption, ParamValue, ParamContext, \
tiramisu_copy
tiramisu_copy, calc_value
from .option import *
from .error import APIError
from .api import Config, MetaConfig, GroupConfig, MixConfig
@ -37,7 +37,8 @@ allfuncs = ['Params',
'Storage',
'list_sessions',
'delete_session',
'tiramisu_copy']
'tiramisu_copy',
'calc_value']
allfuncs.extend(all_options)
del(all_options)
__all__ = tuple(allfuncs)

View File

@ -148,7 +148,7 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
return self._option_bag.option
def type(self):
return self._option_bag.option.get_display_type()
return self._option_bag.option.get_type()
def isleadership(self):
"""Test if option is a leader or a follower"""
@ -203,6 +203,20 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
settings.get_context_properties(),
settings.get_context_properties())
def __call__(self,
path: str,
index: Optional[int]=None) -> 'TiramisuOption':
"""Select an option by path"""
subpath = self._option_bag.option.impl_getname() + '.' + path
subconfig, name = self._subconfig.cfgimpl_get_home_by_path(subpath,
self._option_bag.config_bag)
path = self._option_bag.path + '.' + path
return TiramisuOption(name,
path,
index,
subconfig,
self._option_bag.config_bag)
class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
"""Manage option"""
@ -451,8 +465,9 @@ class _TiramisuOptionValueOption:
if isinstance(value, list):
while undefined in value:
idx = value.index(undefined)
value[idx] = values.getdefaultvalue(self._option_bag,
force_index=idx)
soption_bag = self._option_bag.copy()
soption_bag.index = idx
value[idx] = values.getdefaultvalue(soption_bag)
elif value == undefined:
value = values.getdefaultvalue(self._option_bag)
self._subconfig.setattr(value,
@ -738,17 +753,9 @@ class TiramisuOption(CommonTiramisuOption):
subconfig=subconfig,
config_bag=config_bag)
#__________________________________________________________________________________________________
#
# First part of API:
# - contexts informations:
# - context owner
# - context information
# - context property
# - context permissive
# - forcepermissive
# - unrestraint
# - manage MetaConfig or GroupConfig
class TiramisuContext(TiramisuHelp):

View File

@ -90,11 +90,11 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
return value[index]
return value
except PropertiesOptionError as err:
# raise because must not add value None in carry_out_calculation
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if callbk.notraisepropertyerror:
raise err
raise ConfigError(_('unable to carry out a calculation for "{}"'
', {}').format(option.impl_get_display_name(), err))
', {}').format(option.impl_get_display_name(), err), err)
def carry_out_calculation(option,

View File

@ -1198,12 +1198,13 @@ class KernelMetaConfig(KernelMixConfig):
if not isinstance(child, _CommonConfig):
try:
child = child._config
except:
except Exception:
raise TypeError(_("{}config's children "
"should be config, not {}"
).format(self.impl_type,
type(child)))
if not isinstance(child, (KernelConfig, KernelMetaConfig)):
if __debug__ and not isinstance(child, (KernelConfig,
KernelMetaConfig)):
raise TypeError(_("child must be a Config or MetaConfig"))
if descr is None:
descr = child.cfgimpl_get_description()

View File

@ -116,7 +116,7 @@ class PropertiesOptionError(AttributeError):
self._name,
prop_msg,
msg))
del self._requires, self._opt_type, self._name, self._option_bag
del self._requires, self._opt_type, self._name
del self._settings, self._orig_opt
return self.msg
@ -127,7 +127,11 @@ class ConfigError(Exception):
"""attempt to change an option's owner without a value
or in case of `_cfgimpl_descr` is None
or if a calculation cannot be carried out"""
pass
def __init__(self,
exp,
ori_err=None):
super().__init__(exp)
self.ori_err = ori_err
class ConflictError(Exception):

View File

@ -12,6 +12,9 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Any, List, Optional
from operator import add, mul, sub, truediv
from .setting import undefined
from .i18n import _
@ -46,9 +49,12 @@ class Param:
class ParamOption(Param):
__slots__ = ('option', 'notraisepropertyerror')
def __init__(self, option, notraisepropertyerror=False):
if not hasattr(option, 'impl_is_symlinkoption'):
__slots__ = ('option',
'notraisepropertyerror')
def __init__(self,
option: 'Option',
notraisepropertyerror: bool=False) -> None:
if __debug__ and not hasattr(option, 'impl_is_symlinkoption'):
raise ValueError(_('paramoption needs an option not {}').format(type(option)))
if option.impl_is_symlinkoption():
cur_opt = option.impl_getopt()
@ -79,3 +85,240 @@ class ParamIndex(Param):
def tiramisu_copy(val): # pragma: no cover
return val
def calc_value(*args: List[Any],
multi: bool=False,
default: Any=undefined,
condition: Any=undefined,
expected: Any=undefined,
condition_operator: str='AND',
allow_none: bool=False,
remove_duplicate_value: bool=False,
join: Optional[str]=None,
min_args_len: Optional[int]=None,
operator: Optional[str]=None,
index: Optional[int]=None,
**kwargs) -> Any:
"""calculate value
:param multi: value returns must be a list of value
:param default: default value if condition is not matched or if args is empty
if there is more than one default value, set default_0, default_1, ...
:param condition: test if condition is equal to expected value
if there is more than one condition, set condition_0, condition_1, ...
:param expected: value expected for all conditions
if expected value is different between condition, set expected_0, expected_1, ...
:param condition_operator: OR or AND operator for condition
:param allow_none: if False, do not return list in None is present in list
:param remove_duplicate_value: if True, remote duplicated value
:param join: join all args with specified characters
:param min_args_len: if number of arguments is smaller than this value, return default value
:param operator: operator
examples:
* you want to copy value from an option to an other option:
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption
>>> val1 = StrOption('val1', '', 'val1')
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1)))
>>> od = OptionDescription('root', '', [val1, val2])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val1'}
* you want to copy values from two options in one multi option
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', "", 'val1')
>>> val2 = StrOption('val2', "", 'val2')
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True)))
>>> od = OptionDescription('root', '', [val1, val2, val3])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val2', 'val3': ['val1', 'val2']}
* you want to copy a value from an option is it not disabled, otherwise set 'default_value'
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', '', 'val1')
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True), default=ParamValue('default_value')))
>>> od = OptionDescription('root', '', [val1, val2])
>>> cfg = Config(od)
>>> cfg.property.read_write()
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val1'}
>>> cfg.option('val1').property.add('disabled')
>>> cfg.value.dict()
{'val2': 'default_value'}
* you want to copy value from an option is an other is True, otherwise set 'default_value'
>>> from tiramisu import calc_value, BoolOption, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> boolean = BoolOption('boolean', '', True)
>>> val1 = StrOption('val1', '', 'val1')
>>> val2 = StrOption('val2', '', callback=calc_value, callback_params=Params(ParamOption(val1, True),
... default=ParamValue('default_value'),
... condition=ParamOption(boolean),
... expected=ParamValue(True)))
>>> od = OptionDescription('root', '', [boolean, val1, val2])
>>> cfg = Config(od)
>>> cfg.property.read_write()
>>> cfg.value.dict()
{'boolean': True, 'val1': 'val1', 'val2': 'val1'}
>>> cfg.option('boolean').value.set(False)
>>> cfg.value.dict()
{'boolean': False, 'val1': 'val1', 'val2': 'default_value'}
* you want to copy option even if None is present
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', "", 'val1')
>>> val2 = StrOption('val2', "")
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), allow_none=ParamValue(True)))
>>> od = OptionDescription('root', '', [val1, val2, val3])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 'val1', 'val2': None, 'val3': ['val1', None]}
* you want uniq value
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', "", 'val1')
>>> val2 = StrOption('val2', "", 'val1')
>>> val3 = StrOption('val3', "", multi=True, callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), multi=ParamValue(True), remove_duplicate_value=ParamValue(True)))
>>> od = OptionDescription('root', '', [val1, val2, val3])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val1', 'val3': ['val1']}
* you want to join two values with '.'
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', "", 'val1')
>>> val2 = StrOption('val2', "", 'val2')
>>> val3 = StrOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), join=ParamValue('.')))
>>> od = OptionDescription('root', '', [val1, val2, val3])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val2', 'val3': 'val1.val2'}
* you want join three values, only if almost three values are set
>>> from tiramisu import calc_value, StrOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = StrOption('val1', "", 'val1')
>>> val2 = StrOption('val2', "", 'val2')
>>> val3 = StrOption('val3', "", 'val3')
>>> val4 = StrOption('val4', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2), ParamOption(val3, True)), join=ParamValue('.'), min_args_len=ParamValue(3)))
>>> od = OptionDescription('root', '', [val1, val2, val3, val4])
>>> cfg = Config(od)
>>> cfg.property.read_write()
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'}
>>> cfg.option('val3').property.add('disabled')
>>> cfg.value.dict()
{'val1': 'val1', 'val2': 'val2', 'val4': ''}
* you want to add all values
>>> from tiramisu import calc_value, IntOption, OptionDescription, Config, Params, ParamOption, ParamValue
>>> val1 = IntOption('val1', "", 1)
>>> val2 = IntOption('val2', "", 2)
>>> val3 = IntOption('val3', "", callback=calc_value, callback_params=Params((ParamOption(val1), ParamOption(val2)), operator=ParamValue('add')))
>>> od = OptionDescription('root', '', [val1, val2, val3])
>>> cfg = Config(od)
>>> cfg.value.dict()
{'val1': 1, 'val2': 2, 'val3': 3}
"""
def value_from_kwargs(value: Any, pattern: str, to_dict: bool=False) -> Any:
# if value attribute exist return it's value
# otherwise pattern_0, pattern_1, ...
# otherwise undefined
if value is not undefined:
if to_dict == 'all':
returns = {0: value}
else:
returns = value
else:
kwargs_matches = {}
len_pattern = len(pattern)
for key in kwargs.keys():
if key.startswith(pattern):
index = int(key[len_pattern:])
kwargs_matches[index] = kwargs[key]
if not kwargs_matches:
return undefined
keys = sorted(kwargs_matches)
if to_dict:
returns = {}
else:
returns = []
for key in keys:
if to_dict:
returns[key] = kwargs_matches[key]
else:
returns.append(kwargs_matches[key])
return returns
def is_condition_matches():
calculated_conditions = value_from_kwargs(condition, 'condition_', to_dict='all')
if condition is not undefined:
is_matches = None
calculated_expected = value_from_kwargs(expected, 'expected_', to_dict=True)
for idx, calculated_condition in calculated_conditions.items():
if isinstance(calculated_expected, dict):
current_matches = calculated_condition == calculated_expected[idx]
else:
current_matches = calculated_condition == calculated_expected
if is_matches is None:
is_matches = current_matches
elif condition_operator == 'AND':
is_matches = is_matches and current_matches
elif condition_operator == 'OR':
is_matches = is_matches or current_matches
else:
raise ValueError(_('unexpected {} condition_operator in calc_value').format(condition_operator))
else:
is_matches = True
return is_matches
def get_value():
if not is_condition_matches():
# force to default
value = []
else:
value = list(args)
if min_args_len and not len(value) >= min_args_len:
value = []
if value == []:
# default value
new_default = value_from_kwargs(default, 'default_')
if new_default is not undefined:
if not isinstance(new_default, list):
value = [new_default]
else:
value = new_default
return value
value = get_value()
if not multi:
if join is not None:
value = join.join(value)
elif value and operator:
new_value = value[0]
op = {'mul': mul,
'add': add,
'div': truediv,
'sub': sub}[operator]
for val in value[1:]:
new_value = op(new_value, val)
value = new_value
elif value == []:
value = None
else:
value = value[0]
if isinstance(value, list) and index is not None:
if len(value) > index:
value = value[index]
else:
value = None
elif None in value and not allow_none:
value = []
elif remove_duplicate_value:
new_value = []
for val in value:
if val not in new_value:
new_value.append(val)
value = new_value
return value

View File

@ -15,9 +15,16 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________
from logging import getLogger
from logging import getLogger, DEBUG, basicConfig, StreamHandler, Formatter
import os
log = getLogger('tiramisu')
# import logging
# logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
if os.environ.get('TIRAMISU_DEBUG') == 'True':
log.setLevel(DEBUG)
handler = StreamHandler()
handler.setLevel(DEBUG)
formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)

View File

@ -170,7 +170,7 @@ class Base:
param.option._add_dependency(self)
if type_ == 'validator':
self._has_dependency = True
is_multi = self.impl_is_dynoptiondescription() or self.impl_is_multi()
is_multi = self.impl_is_optiondescription() or self.impl_is_multi()
func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator)
calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value)
# remove knowned kwargs
@ -253,13 +253,14 @@ class Base:
def _impl_set_callback(self,
callback: Callable,
callback_params: Optional[Params]=None) -> None:
if callback is None and callback_params is not None:
raise ValueError(_("params defined for a callback function but "
"no callback defined"
' yet for option "{0}"').format(
self.impl_getname()))
self._validate_calculator(callback,
callback_params)
if __debug__:
if callback is None and callback_params is not None:
raise ValueError(_("params defined for a callback function but "
"no callback defined"
' yet for option "{0}"').format(
self.impl_getname()))
self._validate_calculator(callback,
callback_params)
if callback is not None:
callback_params = self._build_calculator_params(callback,
callback_params,
@ -397,7 +398,10 @@ class BaseOption(Base):
super(BaseOption, self).__setattr__(name, value)
def impl_getpath(self) -> str:
return self._path
try:
return self._path
except AttributeError:
raise AttributeError(_('"{}" not part of any Config').format(self.impl_get_display_name()))
def impl_has_callback(self) -> bool:
"to know if a callback has been defined or not"
@ -440,17 +444,25 @@ def validate_requires_arg(new_option: BaseOption,
the description of the requires dictionary
"""
def get_option(require):
option = require['option']
if option == 'self':
option = new_option
if not isinstance(option, BaseOption):
raise ValueError(_('malformed requirements '
'must be an option in option {0}').format(name))
if not multi and option.impl_is_multi():
raise ValueError(_('malformed requirements '
'multi option must not set '
'as requires of non multi option {0}').format(name))
option._add_dependency(new_option)
if 'option' in require:
option = require['option']
if option == 'self':
option = new_option
if __debug__:
if not isinstance(option, BaseOption):
raise ValueError(_('malformed requirements '
'must be an option in option {0}').format(name))
if not multi and option.impl_is_multi():
raise ValueError(_('malformed requirements '
'multi option must not set '
'as requires of non multi option {0}').format(name))
option._add_dependency(new_option)
else:
callback = require['callback']
callback_params = new_option._build_calculator_params(callback,
require.get('callback_params'),
'callback')
option = (callback, callback_params)
return option
def _set_expected(action,
@ -479,11 +491,11 @@ def validate_requires_arg(new_option: BaseOption,
operator = get_operator(require)
if isinstance(expected, list):
for exp in expected:
if set(exp.keys()) != {'option', 'value'}:
if __debug__ and set(exp.keys()) != {'option', 'value'}:
raise ValueError(_('malformed requirements expected must have '
'option and value for option {0}').format(name))
option = get_option(exp)
if option is not None:
if __debug__:
try:
option._validate(exp['value'], undefined)
except ValueError as err:
@ -499,7 +511,7 @@ def validate_requires_arg(new_option: BaseOption,
operator)
else:
option = get_option(require)
if expected is not None:
if __debug__ and not isinstance(option, tuple) and expected is not None:
try:
option._validate(expected, undefined)
except ValueError as err:
@ -557,25 +569,29 @@ def validate_requires_arg(new_option: BaseOption,
# start parsing all requires given by user (has dict)
# transforme it to a tuple
for require in requires:
if not isinstance(require, dict):
raise ValueError(_("malformed requirements type for option:"
" {0}, must be a dict").format(name))
valid_keys = ('option', 'expected', 'action', 'inverse', 'transitive',
'same_action', 'operator')
unknown_keys = frozenset(require.keys()) - frozenset(valid_keys)
if unknown_keys != frozenset():
raise ValueError(_('malformed requirements for option: {0}'
' unknown keys {1}, must only '
'{2}').format(name,
unknown_keys,
valid_keys))
# prepare all attributes
if not ('expected' in require and isinstance(require['expected'], list)) and \
not ('option' in require and 'expected' in require) or \
'action' not in require:
raise ValueError(_("malformed requirements for option: {0}"
" require must have option, expected and"
" action keys").format(name))
if __debug__:
if not isinstance(require, dict):
raise ValueError(_("malformed requirements type for option:"
" {0}, must be a dict").format(name))
valid_keys = ('option', 'expected', 'action', 'inverse', 'transitive',
'same_action', 'operator', 'callback', 'callback_params')
unknown_keys = frozenset(require.keys()) - frozenset(valid_keys)
if unknown_keys != frozenset():
raise ValueError(_('malformed requirements for option: {0}'
' unknown keys {1}, must only '
'{2}').format(name,
unknown_keys,
valid_keys))
# {'expected': ..., 'option': ..., 'action': ...}
# {'expected': [{'option': ..., 'value': ...}, ...}], 'action': ...}
# {'expected': ..., 'callback': ..., 'action': ...}
if not 'expected' in require or not 'action' in require or \
not (isinstance(require['expected'], list) or \
'option' in require or \
'callback' in require):
raise ValueError(_("malformed requirements for option: {0}"
" require must have option, expected and"
" action keys").format(name))
action = get_action(require)
config_action.add(action)
if action not in ret_requires:

View File

@ -27,6 +27,7 @@ from .option import Option
class BoolOption(Option):
"represents a choice between ``True`` and ``False``"
__slots__ = tuple()
_type = 'boolean'
_display_name = _('boolean')
def _validate(self,

View File

@ -28,6 +28,7 @@ from .option import Option
class BroadcastOption(Option):
__slots__ = tuple()
_type = 'broadcast_address'
_display_name = _('broadcast address')
def _validate(self,

View File

@ -33,6 +33,7 @@ class ChoiceOption(Option):
The option can also have the value ``None``
"""
__slots__ = tuple()
_type = 'choice'
_display_name = _('choice')
def __init__(self,

View File

@ -27,6 +27,7 @@ from .option import Option
class DateOption(Option):
__slots__ = tuple()
_type = 'date'
_display_name = _('date')
def _validate(self,

View File

@ -19,15 +19,14 @@
# the whole pypy projet is under MIT licence
# ____________________________________________________________
import re
from ipaddress import ip_address, IPv4Address
from ipaddress import ip_address
from ..setting import undefined, Undefined, OptionBag
from ..i18n import _
from .option import Option
from .stroption import StrOption
from .ipoption import IPOption
class DomainnameOption(StrOption):
class DomainnameOption(IPOption):
"""represents the choice of a domain name
netbios: for MS domain
hostname: to identify the device
@ -35,6 +34,7 @@ class DomainnameOption(StrOption):
fqdn: with tld, not supported yet
"""
__slots__ = tuple()
_type = 'domainname'
_display_name = _('domain name')
def __init__(self,
@ -43,16 +43,17 @@ class DomainnameOption(StrOption):
default=None,
default_multi=None,
requires=None,
multi=False,
multi: bool=False,
callback=None,
callback_params=None,
validator=None,
validator_params=None,
properties=None,
allow_ip=False,
type_='domainname',
warnings_only=False,
allow_without_dot=False):
allow_ip: bool=False,
cidr: bool=False,
type_: str='domainname',
warnings_only: bool=False,
allow_without_dot=False) -> None:
if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
@ -72,25 +73,29 @@ class DomainnameOption(StrOption):
else:
regexp = r'((?!-)[a-z0-9-]{{1,{0}}})'.format(self._get_len(type_))
if allow_ip:
regexp = r'^(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))$'.format(regexp)
if not cidr:
regexp = r'^(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))$'.format(regexp)
else:
regexp = r'^(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))$'.format(regexp)
else:
regexp = r'^{0}$'.format(regexp)
extra['_domain_re'] = re.compile(regexp)
extra['_has_upper'] = re.compile('[A-Z]')
super(DomainnameOption, self).__init__(name,
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
properties=properties,
warnings_only=warnings_only,
extra=extra)
super().__init__(name,
doc,
default=default,
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_params=validator_params,
properties=properties,
warnings_only=warnings_only,
cidr=cidr,
_extra=extra)
def _get_len(self, type_):
if type_ == 'netbios':
@ -116,9 +121,10 @@ class DomainnameOption(StrOption):
except ValueError:
pass
else:
if self.impl_get_extra('_allow_ip') is True:
return
raise ValueError(_('must not be an IP'))
if self.impl_get_extra('_allow_ip') is False:
raise ValueError(_('must not be an IP'))
# it's an IP so validate with IPOption
return super()._validate(value, option_bag, current_opt)
part_name_length = self._get_len(self.impl_get_extra('_dom_type'))
if self.impl_get_extra('_dom_type') == 'domainname':
if not self.impl_get_extra('_allow_without_dot') and not "." in value:

View File

@ -28,4 +28,5 @@ class EmailOption(RegexpOption):
__slots__ = tuple()
#https://www.w3.org/TR/html-markup/input.email.html#input.email.attrs.value.single.
_regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
_type = 'email'
_display_name = _('email address')

View File

@ -27,4 +27,5 @@ from .stroption import RegexpOption
class FilenameOption(RegexpOption):
__slots__ = tuple()
_regexp = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
_type = 'filename'
_display_name = _('file name')

View File

@ -27,6 +27,7 @@ from .option import Option
class FloatOption(Option):
"represents a choice of a floating point number"
__slots__ = tuple()
_type = 'float'
_display_name = _('float')
def _validate(self,

View File

@ -27,6 +27,7 @@ from .option import Option
class IntOption(Option):
"represents a choice of an integer"
__slots__ = tuple()
_type = 'integer'
_display_name = _('integer')
def __init__(self,

View File

@ -32,6 +32,7 @@ from .networkoption import NetworkOption
class IPOption(StrOption):
"represents the choice of an ip"
__slots__ = tuple()
_type = 'ip'
_display_name = _('IP')
def __init__(self,
@ -49,10 +50,15 @@ class IPOption(StrOption):
private_only=False,
allow_reserved=False,
warnings_only=False,
cidr=False):
extra = {'_private_only': private_only,
'_allow_reserved': allow_reserved,
'_cidr': cidr}
cidr=False,
_extra=None):
if _extra is None:
extra = {}
else:
extra = _extra
extra['_private_only'] = private_only
extra['_allow_reserved'] = allow_reserved
extra['_cidr'] = cidr
super().__init__(name,
doc,
default=default,

View File

@ -109,7 +109,7 @@ class Leadership(OptionDescription):
for requires_ in getattr(self, '_requires', ()):
for require in requires_:
for require_opt, values in require[0]:
if require_opt.impl_is_multi() and require_opt.impl_get_leadership():
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi() and require_opt.impl_get_leadership():
raise ValueError(_('malformed requirements option "{0}" '
'must not be in follower for "{1}"').format(
require_opt.impl_getname(),

View File

@ -19,6 +19,7 @@
# the whole pypy projet is under MIT licence
# ____________________________________________________________
from ipaddress import ip_interface, ip_network
from typing import List
from ..error import ConfigError
from ..setting import undefined, OptionBag, Undefined
@ -30,6 +31,7 @@ from .stroption import StrOption
class NetmaskOption(StrOption):
"represents the choice of a netmask"
__slots__ = tuple()
_type = 'netmask'
_display_name = _('netmask address')
def _validate(self,
@ -49,57 +51,56 @@ class NetmaskOption(StrOption):
raise ValueError()
def _cons_network_netmask(self,
current_opt,
opts,
vals,
warnings_only,
context):
#opts must be (netmask, network) options
current_opt: Option,
opts: List[Option],
vals: List[str],
warnings_only: bool,
context: 'Config'):
if context is undefined and len(vals) != 2:
raise ConfigError(_('network_netmask needs a network and a netmask'))
if None in vals or len(vals) != 2:
return
val_netmask, val_network = vals
opt_netmask, opt_network = opts
try:
ip_network('{0}/{1}'.format(val_network, val_netmask))
except ValueError:
if current_opt == opts[1]:
if current_opt == opt_network:
raise ValueError(_('with netmask "{0}" ("{1}")').format(val_netmask,
opts[0].impl_get_display_name()))
opt_netmask.impl_get_display_name()))
else:
raise ValueError(_('with network "{0}" ("{1}")').format(val_network,
opts[1].impl_get_display_name()))
opt_network.impl_get_display_name()))
def _cons_ip_netmask(self,
current_opt,
opts,
vals,
warnings_only,
context,
_cidr=False):
# opts must be (netmask, ip) options
current_opt: Option,
opts: List[Option],
vals: List[str],
warnings_only: bool,
context: 'config',
_cidr: bool=False):
if context is undefined and len(vals) != 2:
raise ConfigError(_('ip_netmask needs an IP and a netmask'))
if None in vals or len(vals) != 2:
return
msg = None
val_netmask, val_ip = vals
opt_netmask, opt_ip = opts
ip = ip_interface('{0}/{1}'.format(val_ip, val_netmask))
network = ip.network
if ip.ip == network.network_address:
if not _cidr and current_opt == opts[1]:
msg = _('this is a network with netmask "{0}" ("{1}")')
else:
msg = _('IP "{0}" ("{1}") is the network')
elif ip.ip == network.broadcast_address:
if not _cidr and current_opt == opts[1]:
msg = _('this is a broadcast with netmask "{0}" ("{1}")')
else:
msg = _('IP "{0}" ("{1}") is the broadcast')
if msg is not None:
if not _cidr and current_opt == opts[1]:
raise ValueError(msg.format(val_netmask,
opts[0].impl_get_display_name()))
else:
raise ValueError(msg.format(val_ip,
opts[1].impl_get_display_name()))
if not _cidr and current_opt == opt_ip:
if ip.ip == ip.network.network_address:
raise ValueError( _('this is a network with netmask "{0}" ("{1}")'
'').format(val_netmask,
opt_netmask.impl_get_display_name()))
elif ip.ip == ip.network.broadcast_address:
raise ValueError(_('this is a broadcast with netmask "{0}" ("{1}")'
'').format(val_netmask,
opt_netmask.impl_get_display_name()))
else:
if ip.ip == ip.network.network_address:
raise ValueError(_('IP "{0}" ("{1}") is the network'
'').format(val_ip,
opt_ip.impl_get_display_name()))
elif ip.ip == ip.network.broadcast_address:
raise ValueError(_('IP "{0}" ("{1}") is the broadcast'
'').format(val_ip,
opt_ip.impl_get_display_name()))

View File

@ -28,6 +28,7 @@ from .option import Option
class NetworkOption(Option):
"represents the choice of a network"
__slots__ = tuple()
_type = 'network'
_display_name = _('network address')
def __init__(self,

View File

@ -179,6 +179,10 @@ class Option(BaseOption):
def impl_is_dynsymlinkoption(self) -> bool:
return False
def get_type(self) -> str:
# _display_name for compatibility with older version than 3.0rc3
return getattr(self, '_type', self._display_name)
def get_display_type(self) -> str:
return self._display_name
@ -388,13 +392,13 @@ class Option(BaseOption):
def impl_is_leader(self):
leadership = self.impl_get_leadership()
if leadership is None:
return leadership
return self.impl_get_leadership().is_leader(self)
return False
return leadership.is_leader(self)
def impl_is_follower(self):
leadership = self.impl_get_leadership()
if leadership is None:
return leadership
return False
return not leadership.is_leader(self)
def impl_get_leadership(self):

View File

@ -139,7 +139,7 @@ class CacheOptionDescription(BaseOption):
# * current option must be a follower (and only a follower)
# * option in require and current option must be in same leadership
for require_opt, values in require[0]:
if require_opt.impl_is_multi():
if not isinstance(require_opt, tuple) and require_opt.impl_is_multi():
if is_follower is None:
is_follower = option.impl_is_follower()
if is_follower:

View File

@ -28,6 +28,7 @@ from .stroption import StrOption
class PasswordOption(StrOption):
"represents the choice of a password"
__slots__ = tuple()
_type = 'password'
_display_name = _('password')
def _validate(self,

View File

@ -40,6 +40,7 @@ class PortOption(StrOption):
"""
__slots__ = tuple()
port_re = re.compile(r"^[0-9]*$")
_type = 'port'
_display_name = _('port')
def __init__(self,

View File

@ -29,6 +29,7 @@ from .option import Option
class StrOption(Option):
"represents the choice of a string"
__slots__ = tuple()
_type = 'string'
_display_name = _('string')
def _validate(self,

View File

@ -30,6 +30,7 @@ class URLOption(DomainnameOption):
__slots__ = tuple()
proto_re = re.compile(r'(http|https)://')
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
_type = 'url'
_display_name = _('URL')
def _validate(self,

View File

@ -28,4 +28,5 @@ class UsernameOption(RegexpOption):
__slots__ = tuple()
#regexp build with 'man 8 adduser' informations
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
_type = 'username'
_display_name = _('username')

View File

@ -509,39 +509,59 @@ class Settings(object):
exps, action, inverse, transitive, same_action, operator = require
breaked = False
for option, expected in exps:
if option.issubdyn():
option = option.to_dynoption(option_bag.option.rootpath,
option_bag.option.impl_getsuffix())
reqpath = option.impl_getpath()
#FIXME too later!
if reqpath.startswith(option_bag.path + '.'):
raise RequirementError(_("malformed requirements "
"imbrication detected for option:"
" '{0}' with requirement on: "
"'{1}'").format(option_bag.path, reqpath))
idx = None
is_indexed = False
if option.impl_is_follower():
idx = option_bag.index
elif option.impl_is_multi() and option_bag.index is not None:
is_indexed = True
config_bag = option_bag.config_bag.copy()
soption_bag = OptionBag()
soption_bag.set_option(option,
reqpath,
idx,
config_bag)
if option_bag.option == option:
soption_bag.config_bag.unrestraint()
soption_bag.config_bag.remove_validation()
soption_bag.apply_requires = False
if not isinstance(option, tuple):
if option.issubdyn():
option = option.to_dynoption(option_bag.option.rootpath,
option_bag.option.impl_getsuffix())
reqpath = option.impl_getpath()
if __debug__ and reqpath.startswith(option_bag.path + '.'):
# FIXME too later!
raise RequirementError(_("malformed requirements "
"imbrication detected for option:"
" '{0}' with requirement on: "
"'{1}'").format(option_bag.path, reqpath))
idx = None
is_indexed = False
if option.impl_is_follower():
idx = option_bag.index
if idx is None:
continue
elif option.impl_is_leader() and option_bag.index is None:
continue
elif option.impl_is_multi() and option_bag.index is not None:
is_indexed = True
config_bag = option_bag.config_bag.copy()
soption_bag = OptionBag()
soption_bag.set_option(option,
reqpath,
idx,
config_bag)
if option_bag.option == option:
soption_bag.config_bag.unrestraint()
soption_bag.config_bag.remove_validation()
soption_bag.apply_requires = False
else:
soption_bag.config_bag.properties = soption_bag.config_bag.true_properties
soption_bag.config_bag.set_permissive()
else:
soption_bag.config_bag.properties = soption_bag.config_bag.true_properties
soption_bag.config_bag.set_permissive()
if not option_bag.option.impl_is_optiondescription() and option_bag.option.impl_is_follower():
idx = option_bag.index
if idx is None:
continue
is_indexed = False
try:
value = context.getattr(reqpath,
soption_bag)
except PropertiesOptionError as err:
if not isinstance(option, tuple):
value = context.getattr(reqpath,
soption_bag)
else:
value = context.cfgimpl_get_values().carry_out_calculation(option_bag,
option[0],
option[1])
except (PropertiesOptionError, ConfigError) as err:
if isinstance(err, ConfigError):
if not isinstance(err.ori_err, PropertiesOptionError):
raise err
err = err.ori_err
properties = err.proptype
# if not transitive, properties must be verify in current requires
# otherwise if same_action, property must be in properties
@ -582,13 +602,19 @@ class Settings(object):
inverse and value not in expected):
if operator != 'and':
if readable:
if not inverse:
msg = _('the value of "{0}" is {1}')
display_value = display_list(expected, 'or', add_quote=True)
if isinstance(option, tuple):
if not inverse:
msg = _('the calculated value is {0}').format(display_value)
else:
msg = _('the calculated value is not {0}').format(display_value)
else:
msg = _('the value of "{0}" is not {1}')
calc_properties.setdefault(action, []).append(
msg.format(option.impl_get_display_name(),
display_list(expected, 'or', add_quote=True)))
name = option.impl_get_display_name()
if not inverse:
msg = _('the value of "{0}" is {1}').format(name, display_value)
else:
msg = _('the value of "{0}" is not {1}').format(name, display_value)
calc_properties.setdefault(action, []).append(msg)
else:
calc_properties.add(action)
breaked = True

View File

@ -32,16 +32,16 @@ class Properties(Cache):
# properties
def setproperties(self, path, properties):
log.debug('setproperties', path, properties)
log.debug('setproperties %s %s', path, properties)
self._properties[path] = properties
def getproperties(self, path, default_properties):
ret = self._properties.get(path, frozenset(default_properties))
log.debug('getproperties', path, ret)
log.debug('getproperties %s %s', path, ret)
return ret
def delproperties(self, path):
log.debug('delproperties', path)
log.debug('delproperties %s', path)
if path in self._properties:
del(self._properties[path])
@ -64,7 +64,7 @@ class Permissives(Cache):
super(Permissives, self).__init__(storage)
def setpermissives(self, path, permissives):
log.debug('setpermissives', path, permissives)
log.debug('setpermissives %s %s', path, permissives)
if not permissives:
if path in self._permissives:
del self._permissives[path]
@ -73,7 +73,7 @@ class Permissives(Cache):
def getpermissives(self, path=None):
ret = self._permissives.get(path, frozenset())
log.debug('getpermissives', path, ret)
log.debug('getpermissives %s %s', path, ret)
return ret
def exportation(self):
@ -86,6 +86,6 @@ class Permissives(Cache):
self._permissives = permissives
def delpermissive(self, path):
log.debug('delpermissive', path)
log.debug('delpermissive %s', path)
if path in self._permissives:
del(self._permissives[path])

View File

@ -76,7 +76,7 @@ class Values(Cache):
"""set value for a path
a specified value must be associated to an owner
"""
log.debug('setvalue', path, value, owner, index, id(self))
log.debug('setvalue %s %s %s %s %s', path, value, owner, index, id(self))
values = []
vidx = None
@ -97,7 +97,7 @@ class Values(Cache):
return: boolean
"""
has_path = path in self._values[0]
log.debug('hasvalue', path, index, has_path, id(self))
log.debug('hasvalue %s %s %s %s', path, index, has_path, id(self))
if index is None:
return has_path
elif has_path:
@ -110,7 +110,7 @@ class Values(Cache):
"""
_values == ((path1, path2), ((idx1_1, idx1_2), None), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
log.debug('reduce_index', path, index, id(self))
log.debug('reduce_index %s %s %s', path, index, id(self))
path_idx = self._values[0].index(path)
# get the "index" position
subidx = self._values[1][path_idx].index(index)
@ -129,7 +129,7 @@ class Values(Cache):
path,
index,
commit):
log.debug('resetvalue_index', path, index, id(self))
log.debug('resetvalue_index %s %s %s', path, index, id(self))
def _resetvalue(nb):
values_idx = list(values[nb])
del(values_idx[path_idx])
@ -163,7 +163,7 @@ class Values(Cache):
commit):
"""remove value means delete value in storage
"""
log.debug('resetvalue', path, id(self))
log.debug('resetvalue %s %s', path, id(self))
def _resetvalue(nb):
lst = list(self._values[nb])
lst.pop(idx)
@ -216,7 +216,7 @@ class Values(Cache):
with_value)
if owner is undefined:
owner = default
log.debug('getvalue', path, index, value, owner, id(self))
log.debug('getvalue %s %s %s %s %s', path, index, value, owner, id(self))
if with_value:
return owner, value
else:

View File

@ -81,7 +81,7 @@ class Permissives(Sqlite3DB):
# permissive
def setpermissives(self, path, permissive):
path = self._sqlite_encode_path(path)
log.debug('setpermissive', path, permissive, id(self))
log.debug('setpermissive %s %s %s', path, permissive, id(self))
self._storage.execute("DELETE FROM permissive WHERE path = ? AND session_id = ?",
(path, self._session_id),
False)
@ -99,7 +99,7 @@ class Permissives(Sqlite3DB):
ret = frozenset()
else:
ret = frozenset(self._sqlite_decode(permissives[0]))
log.debug('getpermissive', path, ret, id(self))
log.debug('getpermissive %s %s %s', path, ret, id(self))
return ret
def delpermissive(self, path):

View File

@ -54,7 +54,7 @@ class Values(Sqlite3DB):
"""set value for an option
a specified value must be associated to an owner
"""
log.debug('setvalue', path, value, owner, index, commit)
log.debug('setvalue %s %s %s %s %s', path, value, owner, index, commit)
path = self._sqlite_encode_path(path)
if index is not None:
self.resetvalue_index(path,
@ -81,7 +81,7 @@ class Values(Sqlite3DB):
"""if opt has a value
return: boolean
"""
log.debug('hasvalue', path, index)
log.debug('hasvalue %s %s', path, index)
path = self._sqlite_encode_path(path)
return self._sqlite_select(path, index) is not None
@ -91,7 +91,7 @@ class Values(Sqlite3DB):
_values == ((path1, path2), ((idx1_1, idx1_2), None),
((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
log.debug('reduce_index', path, index, id(self))
log.debug('reduce_index %s %s %s', path, index, id(self))
self._storage.execute("UPDATE value SET idx = ? WHERE path = ? and idx = ? "
"AND session_id = ?",
(index - 1, path, index, self._session_id))
@ -102,7 +102,7 @@ class Values(Sqlite3DB):
commit=True):
"""remove value means delete value in storage
"""
log.debug('resetvalue_index', path, index, commit)
log.debug('resetvalue_index %s %s %s', path, index, commit)
path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ? AND idx = ?",
(path, self._session_id, index),
@ -113,7 +113,7 @@ class Values(Sqlite3DB):
commit):
"""remove value means delete value in storage
"""
log.debug('resetvalue', path, commit)
log.debug('resetvalue %s %s', path, commit)
path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id),
@ -126,7 +126,7 @@ class Values(Sqlite3DB):
index=None):
"""change owner for an option
"""
log.debug('setowner', path, owner, index)
log.debug('setowner %s %s %s', path, owner, index)
path = self._sqlite_encode_path(path)
if index is None:
self._storage.execute("UPDATE value SET owner = ? WHERE path = ? AND session_id = ?",
@ -143,7 +143,7 @@ class Values(Sqlite3DB):
"""get owner for an option
return: owner object
"""
log.debug('getowner', path, default, index, with_value)
log.debug('getowner %s %s %s %s', path, default, index, with_value)
path = self._sqlite_encode_path(path)
request = "SELECT owner, value FROM value WHERE path = ? AND session_id = ?"
if index is not None:
@ -178,7 +178,7 @@ class Values(Sqlite3DB):
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
log.debug('set_information', key, value)
log.debug('set_information %s %s', key, value)
path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?",
(key, self._session_id, path),
@ -191,7 +191,7 @@ class Values(Sqlite3DB):
:param key: the item string (ex: "help")
"""
log.debug('get_information', key, default)
log.debug('get_information %s %s', key, default)
path = self._sqlite_encode_path(path)
value = self._storage.select("SELECT value FROM information WHERE key = ? AND "
"session_id = ? AND path = ?",
@ -205,7 +205,7 @@ class Values(Sqlite3DB):
return self._sqlite_decode(value[0])
def del_information(self, path, key, raises):
log.debug('del_information', key, raises)
log.debug('del_information %s %s', key, raises)
path = self._sqlite_encode_path(path)
if raises and self._storage.select("SELECT value FROM information WHERE key = ? "
"AND session_id = ? AND path = ?",
@ -282,7 +282,7 @@ class Values(Sqlite3DB):
def get_max_length(self,
path):
log.debug('get_max_length', path)
log.debug('get_max_length %s', path)
val_max = self._storage.select("SELECT max(idx) FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id), False)
if val_max[0][0] is None:

View File

@ -75,16 +75,16 @@ class Cache(DictCache):
'expire' in self_props):
ntime = int(time())
if timestamp + expiration_time >= ntime:
log.debug('getcache in cache (1)', path, value, _display_classname(self),
log.debug('getcache in cache (1) %s %s %s %s %s', path, value, _display_classname(self),
id(self), index)
return True, value
else:
log.debug('getcache expired value for path {} < {}'.format(
timestamp + expiration_time, ntime))
# if expired, remove from cache
#self.delcache(path)
#else:
# log.debug('getcache expired value for path {} < {}'.format(
# timestamp + expiration_time, ntime))
# # if expired, remove from cache
# #self.delcache(path)
else:
log.debug('getcache in cache (2)', path, value, _display_classname(self),
log.debug('getcache in cache (2) %s %s %s %s %s', path, value, _display_classname(self),
id(self), index)
return True, value
log.debug('getcache {} with index {} not in {} cache'.format(path, index,
@ -94,13 +94,13 @@ class Cache(DictCache):
def delcache(self, path):
"""remove cache for a specified path
"""
log.debug('delcache', path, _display_classname(self), id(self))
log.debug('delcache %s %s %s', path, _display_classname(self), id(self))
if path in self._cache:
self._delcache(path)
def reset_all_cache(self):
"empty the cache"
log.debug('reset_all_cache', _display_classname(self), id(self))
log.debug('reset_all_cache %s %s', _display_classname(self), id(self))
self._reset_all_cache()
def get_cached(self):

View File

@ -16,10 +16,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________
import weakref
from typing import Optional
from typing import Optional, Any, Callable
from .error import ConfigError, PropertiesOptionError
from .setting import owners, undefined, forbidden_owners, OptionBag, ConfigBag
from .autolib import carry_out_calculation
from .function import Params
from .i18n import _
@ -135,26 +136,48 @@ class Values(object):
return self.getdefaultvalue(option_bag)
def getdefaultvalue(self,
option_bag,
force_index: Optional[int]=None):
option_bag):
"""get default value:
- get meta config value or
- get calculated value or
- get default value
:param opt: the `option.Option()` object
:param path: path for `option.Option()` object
:type path: str
:param index: index of a multi/submulti
:type index: int
:returns: default value
"""
moption_bag = self._get_meta(option_bag)
if moption_bag:
# retrieved value from meta config
return moption_bag.config_bag.context.cfgimpl_get_values().get_cached_value(moption_bag)
if option_bag.option.impl_has_callback():
# default value is a calculated value
value = self.calculate_value(option_bag)
if value is not undefined:
return value
# now try to get default value:
value = option_bag.option.impl_getdefault()
# - if option is a submulti, return a list a list
# - if option is a multi, return a list
# - default value
if option_bag.option.impl_is_multi() and option_bag.index is not None:
# if index, must return good value for this index
if len(value) > option_bag.index:
value = value[option_bag.index]
else:
# no value for this index, retrieve default multi value
# default_multi is already a list for submulti
value = option_bag.option.impl_getdefault_multi()
return value
def calculate_value(self,
option_bag: OptionBag) -> Any:
def _reset_cache(_value):
if not 'expire' in option_bag.properties:
return
is_cache, cache_value = self._p_.getcache(option_bag.path,
None,
index,
config_bag.properties,
option_bag.index,
option_bag.config_bag.properties,
option_bag.properties,
'value')
if not is_cache or cache_value == _value:
@ -162,75 +185,55 @@ class Values(object):
# so do not invalidate cache
return
# calculated value is a new value, so reset cache
config_bag.context.cfgimpl_reset_cache(option_bag)
option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
config_bag = option_bag.config_bag
opt = option_bag.option
if force_index is not None:
index = force_index
else:
index = option_bag.index
moption_bag = self._get_meta(option_bag)
if moption_bag:
# retrieved value from meta config
return moption_bag.config_bag.context.cfgimpl_get_values().get_cached_value(moption_bag)
if opt.impl_has_callback():
# if value has callback, calculate value
callback, callback_params = opt.impl_get_callback()
value = carry_out_calculation(opt,
callback=callback,
callback_params=callback_params,
index=index,
config_bag=config_bag,
fromconsistency=option_bag.fromconsistency)
if isinstance(value, list) and index is not None:
# if value is a list and index is set
if opt.impl_is_submulti() and (value == [] or not isinstance(value[0], list)):
# return value only if it's a submulti and not a list of list
_reset_cache(value)
return value
if len(value) > index:
# return the value for specified index if found
_reset_cache(value[index])
return value[index]
# there is no calculate value for this index,
# so return an other default value
else:
if opt.impl_is_submulti():
if isinstance(value, list):
# value is a list, but no index specified
if (value != [] and not isinstance(value[0], list)):
# if submulti, return a list of value
value = [value]
elif index is not None:
# if submulti, return a list of value
value = [value]
else:
# return a list of list for a submulti
value = [[value]]
elif opt.impl_is_multi() and not isinstance(value, list) and index is None:
# return a list for a multi
value = [value]
# if value has callback, calculate value
callback, callback_params = option_bag.option.impl_get_callback()
value = self.carry_out_calculation(option_bag,
callback,
callback_params)
if isinstance(value, list) and option_bag.index is not None:
# if value is a list and index is set
if option_bag.option.impl_is_submulti() and (value == [] or not isinstance(value[0], list)):
# return value only if it's a submulti and not a list of list
_reset_cache(value)
return value
if len(value) > option_bag.index:
# return the value for specified index if found
_reset_cache(value[option_bag.index])
return value[option_bag.index]
# there is no calculate value for this index,
# so return an other default value
else:
if option_bag.option.impl_is_submulti():
if isinstance(value, list):
# value is a list, but no index specified
if (value != [] and not isinstance(value[0], list)):
# if submulti, return a list of value
value = [value]
elif option_bag.index is not None:
# if submulti, return a list of value
value = [value]
else:
# return a list of list for a submulti
value = [[value]]
elif option_bag.option.impl_is_multi() and not isinstance(value, list) and option_bag.index is None:
# return a list for a multi
value = [value]
_reset_cache(value)
return value
return undefined
# now try to get default value:
# - if opt is a submulti, return a list a list
# - if opt is a multi, return a list
# - default value
value = opt.impl_getdefault()
if opt.impl_is_multi() and index is not None:
# if index, must return good value for this index
if len(value) > index:
value = value[index]
else:
# no value for this index, retrieve default multi value
# default_multi is already a list for submulti
value = opt.impl_getdefault_multi()
return value
def carry_out_calculation(self,
option_bag: OptionBag,
callback: Callable,
callback_params: Optional[Params]) -> Any:
return carry_out_calculation(option_bag.option,
callback=callback,
callback_params=callback_params,
index=option_bag.index,
config_bag=option_bag.config_bag,
fromconsistency=option_bag.fromconsistency)
def isempty(self,
opt,
value,

View File

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tiramisu\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-02-25 08:47+CET\n"
"POT-Creation-Date: 2019-03-05 08:46+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: Tiramisu's team <egarette@cadoles.com>\n"
@ -53,35 +53,35 @@ msgstr "l'index est obligatoire pour l'option suiveuse \"{}\""
msgid "unknown method {}"
msgstr "méthode {} inconnue"
#: tiramisu/api.py:346
#: tiramisu/api.py:360
msgid "cannot add this property: \"{0}\""
msgstr "ne peut pas ajouter cette propriété : \"{0}\""
#: tiramisu/api.py:489 tiramisu/config.py:252
#: tiramisu/api.py:503 tiramisu/config.py:252
msgid "can't delete a SymLinkOption"
msgstr "ne peut supprimer une valeur à une SymLinkOption"
#: tiramisu/api.py:622 tiramisu/api.py:1316
#: tiramisu/api.py:636 tiramisu/api.py:1322
msgid "please specify a valid sub function ({})"
msgstr "veuillez spécifier une sous fonction valide ({})"
#: tiramisu/api.py:685 tiramisu/api.py:1139
#: tiramisu/api.py:699 tiramisu/api.py:1145
msgid "unknown list type {}"
msgstr "type de liste inconnue {}"
#: tiramisu/api.py:687 tiramisu/api.py:1141
#: tiramisu/api.py:701 tiramisu/api.py:1147
msgid "unknown group_type: {0}"
msgstr "group_type inconnu: {0}"
#: tiramisu/api.py:968
#: tiramisu/api.py:974
msgid "properties must be a set"
msgstr "une propriété doit être de type set"
#: tiramisu/api.py:974 tiramisu/api.py:996
#: tiramisu/api.py:980 tiramisu/api.py:1002
msgid "unknown when {} (must be in append or remove)"
msgstr "value {} inconsistent (doit être append ou remove)"
#: tiramisu/api.py:986 tiramisu/api.py:1008 tiramisu/config.py:1226
#: tiramisu/api.py:992 tiramisu/api.py:1014 tiramisu/config.py:1227
msgid "unknown type {}"
msgstr "type inconnu {}"
@ -221,20 +221,20 @@ msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
"MetaConfig avec une optiondescription doit avoir un nom comme enfant, pas {}"
#: tiramisu/config.py:1207
#: tiramisu/config.py:1208
msgid "child must be a Config or MetaConfig"
msgstr "enfant doit être une une Config ou une MetaConfig"
#: tiramisu/config.py:1211
#: tiramisu/config.py:1212
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
"toutes les configs d'une metaconfig doivent avoir la même optiondescription"
#: tiramisu/config.py:1224
#: tiramisu/config.py:1225
msgid "config name must be uniq in groupconfig for {0}"
msgstr "le nom de la config doit être unique dans un groupconfig pour {0}"
#: tiramisu/config.py:1249
#: tiramisu/config.py:1250
msgid "cannot find the config {}"
msgstr "ne peut pas trouver la config {0}"
@ -250,11 +250,11 @@ msgstr "ou"
msgid " {} "
msgstr " {} "
#: tiramisu/error.py:103 tiramisu/setting.py:559
#: tiramisu/error.py:103 tiramisu/setting.py:563
msgid "property"
msgstr "de la propriété"
#: tiramisu/error.py:105 tiramisu/setting.py:561
#: tiramisu/error.py:105 tiramisu/setting.py:565
msgid "properties"
msgstr "des propriétés"
@ -349,11 +349,15 @@ msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule"
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr "\"{}\" ({}) l'attribut de l'objet \"{}\" est en lecture seule"
#: tiramisu/option/baseoption.py:447
#: tiramisu/option/baseoption.py:403
msgid "\"{}\" not part of any Config"
msgstr "\"{}\" ne fait pas parti d'une Config"
#: tiramisu/option/baseoption.py:450
msgid "malformed requirements must be an option in option {0}"
msgstr "requirements mal formés doit être une option dans l'option {0}"
#: tiramisu/option/baseoption.py:450
#: tiramisu/option/baseoption.py:453
msgid ""
"malformed requirements multi option must not set as requires of non multi "
"option {0}"
@ -361,59 +365,59 @@ msgstr ""
"requirements mal formés une option multiple ne doit pas être spécifié comme "
"pré-requis à l'option non multiple {0}"
#: tiramisu/option/baseoption.py:483
#: tiramisu/option/baseoption.py:486
msgid ""
"malformed requirements expected must have option and value for option {0}"
msgstr ""
"expected mal formés pour le requirements, doit avoir une option et une "
"valeur pour l'option {0}"
#: tiramisu/option/baseoption.py:490 tiramisu/option/baseoption.py:506
#: tiramisu/option/baseoption.py:493 tiramisu/option/baseoption.py:509
msgid "malformed requirements expected value must be valid for option {0}: {1}"
msgstr ""
"valeur de \"expected\" malformé, doit être valide pour l'option {0} : {1}"
#: tiramisu/option/baseoption.py:520
#: tiramisu/option/baseoption.py:523
msgid ""
"malformed requirements for option: {0} action cannot be force_store_value"
msgstr ""
"requirements mal formés pour l'option : {0} action ne peut pas être "
"force_store_value"
#: tiramisu/option/baseoption.py:528
#: tiramisu/option/baseoption.py:531
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
"requirements mal formés pour l'option : {0} inverse doit être un booléen"
#: tiramisu/option/baseoption.py:535
#: tiramisu/option/baseoption.py:538
msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr ""
"requirements mal formés pour l'option : {0} transitive doit être booléen"
#: tiramisu/option/baseoption.py:542
#: tiramisu/option/baseoption.py:545
msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr ""
"requirements mal formés pour l'option : {0} same_action doit être un booléen"
#: tiramisu/option/baseoption.py:549
#: tiramisu/option/baseoption.py:552
msgid ""
"malformed requirements for option: \"{0}\" operator must be \"or\" or \"and\""
msgstr ""
"requirements mal formés pour l'option : \"{0}\" l'opérateur doit être \"or\" "
"ou \"and\""
#: tiramisu/option/baseoption.py:561
#: tiramisu/option/baseoption.py:564
msgid "malformed requirements type for option: {0}, must be a dict"
msgstr ""
"type requirements malformé pour l'option : {0}, doit être un dictionnaire"
#: tiramisu/option/baseoption.py:567
#: tiramisu/option/baseoption.py:570
msgid "malformed requirements for option: {0} unknown keys {1}, must only {2}"
msgstr ""
"requirements mal formés pour l'option : {0} clefs inconnues {1}, doit "
"seulement avoir {2}"
#: tiramisu/option/baseoption.py:576
#: tiramisu/option/baseoption.py:579
msgid ""
"malformed requirements for option: {0} require must have option, expected "
"and action keys"
@ -431,7 +435,7 @@ msgstr "adresse broadcast"
#: tiramisu/option/broadcastoption.py:38 tiramisu/option/dateoption.py:37
#: tiramisu/option/domainnameoption.py:113 tiramisu/option/ipoption.py:77
#: tiramisu/option/netmaskoption.py:40 tiramisu/option/networkoption.py:67
#: tiramisu/option/netmaskoption.py:41 tiramisu/option/networkoption.py:67
#: tiramisu/option/passwordoption.py:38 tiramisu/option/portoption.py:106
#: tiramisu/option/urloption.py:40
msgid "invalid string"
@ -653,7 +657,7 @@ msgstr ""
"requirement mal formé pour l'option \"{0}\" ne doit pas être dans une "
"suiveuse pour \"{1}\""
#: tiramisu/option/netmaskoption.py:33
#: tiramisu/option/netmaskoption.py:34
msgid "netmask address"
msgstr "adresse netmask"
@ -661,11 +665,11 @@ msgstr "adresse netmask"
msgid "network_netmask needs a network and a netmask"
msgstr "network_netmask nécessite un réseau et un masque de réseau"
#: tiramisu/option/netmaskoption.py:67
#: tiramisu/option/netmaskoption.py:68
msgid "with netmask \"{0}\" (\"{1}\")"
msgstr "avec le masque \"{0}\" (\"{1}\")"
#: tiramisu/option/netmaskoption.py:70
#: tiramisu/option/netmaskoption.py:71
msgid "with network \"{0}\" (\"{1}\")"
msgstr "avec le réseau \"{0}\" (\"{1}\")"
@ -673,19 +677,19 @@ msgstr "avec le réseau \"{0}\" (\"{1}\")"
msgid "ip_netmask needs an IP and a netmask"
msgstr "ip_netmask nécessite une IP et un masque de réseau"
#: tiramisu/option/netmaskoption.py:91
#: tiramisu/option/netmaskoption.py:90
msgid "this is a network with netmask \"{0}\" (\"{1}\")"
msgstr "c'est une adresse réseau avec le masque \"{0}\" (\"{1}\")"
#: tiramisu/option/netmaskoption.py:93
msgid "IP \"{0}\" (\"{1}\") is the network"
msgstr "IP \"{0}\" (\"{1}\") est le réseau"
#: tiramisu/option/netmaskoption.py:96
#: tiramisu/option/netmaskoption.py:94
msgid "this is a broadcast with netmask \"{0}\" (\"{1}\")"
msgstr "c'est une adresse broadcast avec le masque \"{0}\" (\"{1}\")"
#: tiramisu/option/netmaskoption.py:98
#: tiramisu/option/netmaskoption.py:99
msgid "IP \"{0}\" (\"{1}\") is the network"
msgstr "IP \"{0}\" (\"{1}\") est le réseau"
#: tiramisu/option/netmaskoption.py:103
msgid "IP \"{0}\" (\"{1}\") is the broadcast"
msgstr "IP \"{0}\" (\"{1}\") est l'adresse de broadcast"
@ -831,7 +835,7 @@ msgstr ""
msgid "the dynoption \"{0}\" cannot have \"force_store_value\" property"
msgstr "la dynoption \"{0}\" ne peut avoir la propriété \"force_store_value\""
#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:639
#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:643
msgid ""
"a leader ({0}) cannot have \"force_default_on_freeze\" or "
"\"force_metaconfig_on_freeze\" property without \"frozen\""
@ -982,48 +986,48 @@ msgstr ""
"imbrication de requirements mal formés detectée pour l'option : '{0}' avec "
"requirement sur : '{1}'"
#: tiramisu/setting.py:562
#: tiramisu/setting.py:566
msgid ""
"cannot access to option \"{0}\" because required option \"{1}\" has {2} {3}"
msgstr ""
"ne peut accéder à l'option \"{0}\" parce que l'option requise \"{1}\" a {2} "
"{3}"
#: tiramisu/setting.py:586
#: tiramisu/setting.py:590
msgid "the value of \"{0}\" is {1}"
msgstr "la valeur de \"{0}\" est {1}"
#: tiramisu/setting.py:588
#: tiramisu/setting.py:592
msgid "the value of \"{0}\" is not {1}"
msgstr "la valeur de \"{0}\" n'est pas {1}"
#: tiramisu/setting.py:629
#: tiramisu/setting.py:633
msgid "cannot set property {} for option \"{}\" this property is calculated"
msgstr ""
"ne peut ajouter la propriété {} pour l'option \"{}\" cette propriété est "
"calculée"
#: tiramisu/setting.py:634
#: tiramisu/setting.py:638
msgid "can't assign property to the symlinkoption \"{}\""
msgstr "ne peut assigner une propriété à une symlinkoption \"{}\""
#: tiramisu/setting.py:666
#: tiramisu/setting.py:670
msgid "permissive must be a frozenset"
msgstr "une permissive doit être de type frozenset"
#: tiramisu/setting.py:670
#: tiramisu/setting.py:674
msgid "can't assign permissive to the symlinkoption \"{}\""
msgstr "ne peut assigner une permissive à la symlinkoption \"{}\""
#: tiramisu/setting.py:677
#: tiramisu/setting.py:681
msgid "cannot add those permissives: {0}"
msgstr "ne peut ajouter ces permissives : {0}"
#: tiramisu/setting.py:694
#: tiramisu/setting.py:698
msgid "can't reset properties to the symlinkoption \"{}\""
msgstr "ne peut réinitialiser les propriétés de la symlinkoption \"{}\""
#: tiramisu/setting.py:709
#: tiramisu/setting.py:713
msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr "ne peut réinitialiser les permissive de la symlinkoption \"{}\""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2019-02-25 08:47+CET\n"
"POT-Creation-Date: 2019-03-05 08:46+CET\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -51,35 +51,35 @@ msgstr ""
msgid "unknown method {}"
msgstr ""
#: tiramisu/api.py:346
#: tiramisu/api.py:360
msgid "cannot add this property: \"{0}\""
msgstr ""
#: tiramisu/api.py:489 tiramisu/config.py:252
#: tiramisu/api.py:503 tiramisu/config.py:252
msgid "can't delete a SymLinkOption"
msgstr ""
#: tiramisu/api.py:622 tiramisu/api.py:1316
#: tiramisu/api.py:636 tiramisu/api.py:1322
msgid "please specify a valid sub function ({})"
msgstr ""
#: tiramisu/api.py:685 tiramisu/api.py:1139
#: tiramisu/api.py:699 tiramisu/api.py:1145
msgid "unknown list type {}"
msgstr ""
#: tiramisu/api.py:687 tiramisu/api.py:1141
#: tiramisu/api.py:701 tiramisu/api.py:1147
msgid "unknown group_type: {0}"
msgstr ""
#: tiramisu/api.py:968
#: tiramisu/api.py:974
msgid "properties must be a set"
msgstr ""
#: tiramisu/api.py:974 tiramisu/api.py:996
#: tiramisu/api.py:980 tiramisu/api.py:1002
msgid "unknown when {} (must be in append or remove)"
msgstr ""
#: tiramisu/api.py:986 tiramisu/api.py:1008 tiramisu/config.py:1226
#: tiramisu/api.py:992 tiramisu/api.py:1014 tiramisu/config.py:1227
msgid "unknown type {}"
msgstr ""
@ -195,19 +195,19 @@ msgstr ""
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
#: tiramisu/config.py:1207
#: tiramisu/config.py:1208
msgid "child must be a Config or MetaConfig"
msgstr ""
#: tiramisu/config.py:1211
#: tiramisu/config.py:1212
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/config.py:1224
#: tiramisu/config.py:1225
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
#: tiramisu/config.py:1249
#: tiramisu/config.py:1250
msgid "cannot find the config {}"
msgstr ""
@ -223,11 +223,11 @@ msgstr ""
msgid " {} "
msgstr ""
#: tiramisu/error.py:103 tiramisu/setting.py:559
#: tiramisu/error.py:103 tiramisu/setting.py:563
msgid "property"
msgstr ""
#: tiramisu/error.py:105 tiramisu/setting.py:561
#: tiramisu/error.py:105 tiramisu/setting.py:565
msgid "properties"
msgstr ""
@ -316,51 +316,55 @@ msgstr ""
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr ""
#: tiramisu/option/baseoption.py:447
msgid "malformed requirements must be an option in option {0}"
#: tiramisu/option/baseoption.py:403
msgid "\"{}\" not part of any Config"
msgstr ""
#: tiramisu/option/baseoption.py:450
msgid "malformed requirements must be an option in option {0}"
msgstr ""
#: tiramisu/option/baseoption.py:453
msgid "malformed requirements multi option must not set as requires of non multi option {0}"
msgstr ""
#: tiramisu/option/baseoption.py:483
#: tiramisu/option/baseoption.py:486
msgid "malformed requirements expected must have option and value for option {0}"
msgstr ""
#: tiramisu/option/baseoption.py:490 tiramisu/option/baseoption.py:506
#: tiramisu/option/baseoption.py:493 tiramisu/option/baseoption.py:509
msgid "malformed requirements expected value must be valid for option {0}: {1}"
msgstr ""
#: tiramisu/option/baseoption.py:520
#: tiramisu/option/baseoption.py:523
msgid "malformed requirements for option: {0} action cannot be force_store_value"
msgstr ""
#: tiramisu/option/baseoption.py:528
#: tiramisu/option/baseoption.py:531
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
#: tiramisu/option/baseoption.py:535
#: tiramisu/option/baseoption.py:538
msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr ""
#: tiramisu/option/baseoption.py:542
#: tiramisu/option/baseoption.py:545
msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr ""
#: tiramisu/option/baseoption.py:549
#: tiramisu/option/baseoption.py:552
msgid "malformed requirements for option: \"{0}\" operator must be \"or\" or \"and\""
msgstr ""
#: tiramisu/option/baseoption.py:561
#: tiramisu/option/baseoption.py:564
msgid "malformed requirements type for option: {0}, must be a dict"
msgstr ""
#: tiramisu/option/baseoption.py:567
#: tiramisu/option/baseoption.py:570
msgid "malformed requirements for option: {0} unknown keys {1}, must only {2}"
msgstr ""
#: tiramisu/option/baseoption.py:576
#: tiramisu/option/baseoption.py:579
msgid "malformed requirements for option: {0} require must have option, expected and action keys"
msgstr ""
@ -374,7 +378,7 @@ msgstr ""
#: tiramisu/option/broadcastoption.py:38 tiramisu/option/dateoption.py:37
#: tiramisu/option/domainnameoption.py:113 tiramisu/option/ipoption.py:77
#: tiramisu/option/netmaskoption.py:40 tiramisu/option/networkoption.py:67
#: tiramisu/option/netmaskoption.py:41 tiramisu/option/networkoption.py:67
#: tiramisu/option/passwordoption.py:38 tiramisu/option/portoption.py:106
#: tiramisu/option/urloption.py:40
msgid "invalid string"
@ -576,7 +580,7 @@ msgstr ""
msgid "malformed requirements option \"{0}\" must not be in follower for \"{1}\""
msgstr ""
#: tiramisu/option/netmaskoption.py:33
#: tiramisu/option/netmaskoption.py:34
msgid "netmask address"
msgstr ""
@ -584,11 +588,11 @@ msgstr ""
msgid "network_netmask needs a network and a netmask"
msgstr ""
#: tiramisu/option/netmaskoption.py:67
#: tiramisu/option/netmaskoption.py:68
msgid "with netmask \"{0}\" (\"{1}\")"
msgstr ""
#: tiramisu/option/netmaskoption.py:70
#: tiramisu/option/netmaskoption.py:71
msgid "with network \"{0}\" (\"{1}\")"
msgstr ""
@ -596,19 +600,19 @@ msgstr ""
msgid "ip_netmask needs an IP and a netmask"
msgstr ""
#: tiramisu/option/netmaskoption.py:91
#: tiramisu/option/netmaskoption.py:90
msgid "this is a network with netmask \"{0}\" (\"{1}\")"
msgstr ""
#: tiramisu/option/netmaskoption.py:93
msgid "IP \"{0}\" (\"{1}\") is the network"
msgstr ""
#: tiramisu/option/netmaskoption.py:96
#: tiramisu/option/netmaskoption.py:94
msgid "this is a broadcast with netmask \"{0}\" (\"{1}\")"
msgstr ""
#: tiramisu/option/netmaskoption.py:98
#: tiramisu/option/netmaskoption.py:99
msgid "IP \"{0}\" (\"{1}\") is the network"
msgstr ""
#: tiramisu/option/netmaskoption.py:103
msgid "IP \"{0}\" (\"{1}\") is the broadcast"
msgstr ""
@ -737,7 +741,7 @@ msgstr ""
msgid "the dynoption \"{0}\" cannot have \"force_store_value\" property"
msgstr ""
#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:639
#: tiramisu/option/optiondescription.py:97 tiramisu/setting.py:643
msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr ""
@ -865,43 +869,43 @@ msgstr ""
msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'"
msgstr ""
#: tiramisu/setting.py:562
#: tiramisu/setting.py:566
msgid "cannot access to option \"{0}\" because required option \"{1}\" has {2} {3}"
msgstr ""
#: tiramisu/setting.py:586
#: tiramisu/setting.py:590
msgid "the value of \"{0}\" is {1}"
msgstr ""
#: tiramisu/setting.py:588
#: tiramisu/setting.py:592
msgid "the value of \"{0}\" is not {1}"
msgstr ""
#: tiramisu/setting.py:629
#: tiramisu/setting.py:633
msgid "cannot set property {} for option \"{}\" this property is calculated"
msgstr ""
#: tiramisu/setting.py:634
#: tiramisu/setting.py:638
msgid "can't assign property to the symlinkoption \"{}\""
msgstr ""
#: tiramisu/setting.py:666
#: tiramisu/setting.py:670
msgid "permissive must be a frozenset"
msgstr ""
#: tiramisu/setting.py:670
#: tiramisu/setting.py:674
msgid "can't assign permissive to the symlinkoption \"{}\""
msgstr ""
#: tiramisu/setting.py:677
#: tiramisu/setting.py:681
msgid "cannot add those permissives: {0}"
msgstr ""
#: tiramisu/setting.py:694
#: tiramisu/setting.py:698
msgid "can't reset properties to the symlinkoption \"{}\""
msgstr ""
#: tiramisu/setting.py:709
#: tiramisu/setting.py:713
msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr ""