Merge branch 'master' into metaconfig

This commit is contained in:
Emmanuel Garette 2013-09-28 22:49:50 +02:00
commit 6b7db20716
33 changed files with 2363 additions and 817 deletions

View File

@ -24,13 +24,13 @@ define gettext
P="pygettext.py" ; \ P="pygettext.py" ; \
fi ; \ fi ; \
$$P -p translations/ -o $(PACKAGE).pot `find $(PACKAGE)/ -name "*.py"` $$P -p translations/ -o $(PACKAGE).pot `find $(PACKAGE)/ -name "*.py"`
endef endef
# Build translation files # Build translation files
define build_translation define build_translation
if [ -d ${1} ]; then \ if [ -d ${1} ]; then \
for f in `find ${1} -name "*.po"`; do \ for f in `find ${1} -name "*.po"`; do \
msgfmt -o `dirname $$f`/`basename -s ".po" $$f`.mo $$f || true; \ msgfmt -o `dirname $$f`/`basename $$f ".po"`.mo $$f || true; \
done; \ done; \
fi fi
endef endef

View File

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

BIN
doc/config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

257
doc/config.svg Normal file
View File

@ -0,0 +1,257 @@
<?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>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from distutils.core import setup from distutils.core import setup
from os.path import dirname, abspath, join, normpath, isdir, basename from os.path import dirname, abspath, join, normpath, isdir
from os import listdir from os import listdir
@ -9,15 +9,16 @@ def fetch_version():
"""Get version from version.in""" """Get version from version.in"""
return file('VERSION', 'r').readline().strip() return file('VERSION', 'r').readline().strip()
def return_storages(): def return_storages():
"returns all the storage plugins that are living in tiramisu/storage" "returns all the storage plugins that are living in tiramisu/storage"
here = dirname(abspath(__file__)) here = dirname(abspath(__file__))
storages_path = normpath(join(here, 'tiramisu', 'storage')) storages_path = normpath(join(here, 'tiramisu', 'storage'))
dir_content = [ content for content in listdir(storages_path) \ dir_content = [content for content in listdir(storages_path)
if not content =='__pycache__'] if not content == '__pycache__']
storages = filter(isdir, [join(storages_path, content) \ storages = filter(isdir, [join(storages_path, content)
for content in dir_content]) for content in dir_content])
storage_list = [basename(storage) for storage in storages] storage_list = ['.'.join(storage.split('/')[-3:]) for storage in storages]
return storage_list return storage_list
packages = ['tiramisu', 'tiramisu.storage'] packages = ['tiramisu', 'tiramisu.storage']
@ -58,6 +59,6 @@ producing flexible and fast options access.
This version requires Python 2.6 or later. This version requires Python 2.6 or later.
""" """,
packages=packages packages=packages
) )

View File

@ -230,3 +230,9 @@ def test_duplicated_option():
root = OptionDescription('root', '', [d1, d2]) root = OptionDescription('root', '', [d1, d2])
#in different OptionDescription #in different OptionDescription
raises(ConflictError, "config = Config(root)") raises(ConflictError, "config = Config(root)")
def test_cannot_assign_value_to_option_description():
descr = make_description()
cfg = Config(descr)
raises(TypeError, "cfg.gc = 3")

View File

@ -116,6 +116,23 @@ def test_find_in_config():
#assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy') #assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy')
def test_find_multi():
b = BoolOption('bool', '', multi=True)
o = OptionDescription('od', '', [b])
conf = Config(o)
raises(AttributeError, "conf.find(byvalue=True)")
raises(AttributeError, "conf.find_first(byvalue=True)")
conf.bool.append(False)
raises(AttributeError, "conf.find(byvalue=True)")
raises(AttributeError, "conf.find_first(byvalue=True)")
conf.bool.append(False)
raises(AttributeError, "conf.find(byvalue=True)")
raises(AttributeError, "conf.find_first(byvalue=True)")
conf.bool.append(True)
assert conf.find(byvalue=True) == [b]
assert conf.find_first(byvalue=True) == b
def test_does_not_find_in_config(): def test_does_not_find_in_config():
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)

View File

@ -7,7 +7,7 @@ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \
def test_ip(): def test_ip():
a = IPOption('a', '') a = IPOption('a', '')
b = IPOption('b', '', only_private=True) b = IPOption('b', '', private_only=True)
od = OptionDescription('od', '', [a, b]) od = OptionDescription('od', '', [a, b])
c = Config(od) c = Config(od)
c.a = '192.168.1.1' c.a = '192.168.1.1'
@ -29,6 +29,15 @@ def test_ip_default():
c.a == '88.88.88.88' c.a == '88.88.88.88'
def test_ip_reserved():
a = IPOption('a', '')
b = IPOption('b', '', allow_reserved=True)
od = OptionDescription('od', '', [a, b])
c = Config(od)
raises(ValueError, "c.a = '226.94.1.1'")
c.b = '226.94.1.1'
def test_network(): def test_network():
a = NetworkOption('a', '') a = NetworkOption('a', '')
od = OptionDescription('od', '', [a]) od = OptionDescription('od', '', [a])

View File

@ -4,19 +4,33 @@ from py.test import raises
from tiramisu.setting import groups from tiramisu.setting import groups
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription StrOption, OptionDescription, SymLinkOption
from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError
def return_val(): def return_val():
return 'val' return 'val'
def return_list(): def return_concat(*args):
return '.'.join(list(args))
def return_list(value=None):
return ['val', 'val'] return ['val', 'val']
def return_value(value): def return_list2(*args):
return list(args)
def return_value(value=None):
return value
def return_value2(*args, **kwargs):
value = list(args)
value.extend(kwargs.values())
return value return value
@ -298,18 +312,73 @@ def test_callback():
def test_callback_value(): def test_callback_value():
val1 = StrOption('val1', "", 'val') val1 = StrOption('val1', "", 'val')
val2 = StrOption('val2', "", callback=return_value, callback_params={'': (('val1', False),)}) val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)})
maconfig = OptionDescription('rootconfig', '', [val1, val2]) val3 = StrOption('val3', "", callback=return_value, callback_params={'': ('yes',)})
val4 = StrOption('val4', "", callback=return_value, callback_params={'value': ((val1, False),)})
val5 = StrOption('val5', "", callback=return_value, callback_params={'value': ('yes',)})
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val1 == 'val' assert cfg.val1 == 'val'
assert cfg.val2 == 'val' assert cfg.val2 == 'val'
assert cfg.val4 == 'val'
cfg.val1 = 'new-val' cfg.val1 = 'new-val'
assert cfg.val1 == 'new-val' assert cfg.val1 == 'new-val'
assert cfg.val2 == 'new-val' assert cfg.val2 == 'new-val'
assert cfg.val4 == 'new-val'
del(cfg.val1) del(cfg.val1)
assert cfg.val1 == 'val' assert cfg.val1 == 'val'
assert cfg.val2 == 'val' assert cfg.val2 == 'val'
assert cfg.val3 == 'yes'
assert cfg.val4 == 'val'
assert cfg.val5 == 'yes'
def test_callback_value_tuple():
val1 = StrOption('val1', "", 'val1')
val2 = StrOption('val2', "", 'val2')
val3 = StrOption('val3', "", callback=return_concat, callback_params={'': ((val1, False), (val2, False))})
val4 = StrOption('val4', "", callback=return_concat, callback_params={'': ('yes', 'no')})
raises(ValueError, "StrOption('val4', '', callback=return_concat, callback_params={'value': ('yes', 'no')})")
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val1 == 'val1'
assert cfg.val2 == 'val2'
assert cfg.val3 == 'val1.val2'
assert cfg.val4 == 'yes.no'
cfg.val1 = 'new-val'
assert cfg.val3 == 'new-val.val2'
del(cfg.val1)
assert cfg.val3 == 'val1.val2'
def test_callback_value_force_permissive():
val1 = StrOption('val1', "", 'val', properties=('disabled',))
val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)})
val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val1, True),)})
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3])
cfg = Config(maconfig)
cfg.read_only()
raises(ConfigError, "cfg.val2")
assert cfg.val3 is None
def test_callback_symlink():
val1 = StrOption('val1', "", 'val')
val2 = SymLinkOption('val2', val1)
val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val2, False),)})
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val1 == 'val'
assert cfg.val3 == 'val'
cfg.val1 = 'new-val'
assert cfg.val1 == 'new-val'
assert cfg.val3 == 'new-val'
del(cfg.val1)
assert cfg.val1 == 'val'
assert cfg.val3 == 'val'
def test_callback_list(): def test_callback_list():
@ -336,21 +405,28 @@ def test_callback_multi():
def test_callback_multi_value(): def test_callback_multi_value():
val1 = StrOption('val1', "", ['val'], multi=True) val1 = StrOption('val1', "", ['val'], multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1', False),)}) val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
maconfig = OptionDescription('rootconfig', '', [val1, val2]) val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), 'yes')})
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val1 == ['val'] assert cfg.val1 == ['val']
assert cfg.val2 == ['val'] assert cfg.val2 == ['val']
assert cfg.val4 == ['val', 'yes']
cfg.val1 = ['new-val'] cfg.val1 = ['new-val']
assert cfg.val1 == ['new-val'] assert cfg.val1 == ['new-val']
assert cfg.val2 == ['new-val'] assert cfg.val2 == ['new-val']
assert cfg.val4 == ['new-val', 'yes']
cfg.val1.append('new-val2') cfg.val1.append('new-val2')
assert cfg.val1 == ['new-val', 'new-val2'] assert cfg.val1 == ['new-val', 'new-val2']
assert cfg.val2 == ['new-val', 'new-val2'] assert cfg.val2 == ['new-val', 'new-val2']
assert cfg.val4 == ['new-val', 'yes', 'new-val2', 'yes']
del(cfg.val1) del(cfg.val1)
assert cfg.val1 == ['val'] assert cfg.val1 == ['val']
assert cfg.val2 == ['val'] assert cfg.val2 == ['val']
assert cfg.val3 == ['yes']
assert cfg.val4 == ['val', 'yes']
def test_callback_multi_list(): def test_callback_multi_list():
@ -455,41 +531,67 @@ def test_callback_master_and_slaves_slave_list():
def test_callback_master_and_slaves_value(): def test_callback_master_and_slaves_value():
val1 = StrOption('val1', "", multi=True) val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1.val1', False),)}) val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
interface1 = OptionDescription('val1', '', [val1, val2]) val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)})
interface1 = OptionDescription('val1', '', [val1, val2, val3, val5])
interface1.impl_set_group_type(groups.master) interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1]) maconfig = OptionDescription('rootconfig', '', [interface1, val4])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val1.val1 == [] assert cfg.val1.val1 == []
assert cfg.val1.val2 == [] assert cfg.val1.val2 == []
assert cfg.val1.val3 == []
assert cfg.val1.val5 == []
# #
cfg.val1.val1 = ['val1'] cfg.val1.val1 = ['val1']
assert cfg.val1.val1 == ['val1'] assert cfg.val1.val1 == ['val1']
assert cfg.val1.val2 == ['val1'] assert cfg.val1.val2 == ['val1']
assert cfg.val1.val3 == ['yes']
assert cfg.val1.val5 == ['val10']
# #
cfg.val1.val1.append('val2') cfg.val1.val1.append('val2')
assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11']
# #
cfg.val1.val1 = ['val1', 'val2', 'val3'] cfg.val1.val1 = ['val1', 'val2', 'val3']
assert cfg.val1.val1 == ['val1', 'val2', 'val3'] assert cfg.val1.val1 == ['val1', 'val2', 'val3']
assert cfg.val1.val2 == ['val1', 'val2', 'val3'] assert cfg.val1.val2 == ['val1', 'val2', 'val3']
assert cfg.val1.val3 == ['yes', 'yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11', None]
# #
cfg.val1.val1.pop(2) cfg.val1.val1.pop(2)
assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11']
# #
cfg.val1.val2 = ['val2', 'val2'] cfg.val1.val2 = ['val2', 'val2']
cfg.val1.val3 = ['val2', 'val2']
cfg.val1.val5 = ['val2', 'val2']
assert cfg.val1.val2 == ['val2', 'val2'] assert cfg.val1.val2 == ['val2', 'val2']
assert cfg.val1.val3 == ['val2', 'val2']
assert cfg.val1.val5 == ['val2', 'val2']
# #
cfg.val1.val1.append('val3') cfg.val1.val1.append('val3')
assert cfg.val1.val2 == ['val2', 'val2', 'val3'] assert cfg.val1.val2 == ['val2', 'val2', 'val3']
assert cfg.val1.val3 == ['val2', 'val2', 'yes']
assert cfg.val1.val5 == ['val2', 'val2', None]
cfg.cfgimpl_get_settings().remove('cache')
cfg.val4 = ['val10', 'val11', 'val12']
#if value is already set, not updated !
cfg.val1.val1.pop(2)
cfg.val1.val1.append('val3')
cfg.val1.val1 = ['val1', 'val2', 'val3']
assert cfg.val1.val5 == ['val2', 'val2', 'val12']
def test_callback_hidden(): def test_callback_hidden():
opt1 = BoolOption('opt1', '') opt1 = BoolOption('opt1', '')
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)})
od1 = OptionDescription('od1', '', [opt1], properties=('hidden',)) od1 = OptionDescription('od1', '', [opt1], properties=('hidden',))
od2 = OptionDescription('od2', '', [opt2]) od2 = OptionDescription('od2', '', [opt2])
maconfig = OptionDescription('rootconfig', '', [od1, od2]) maconfig = OptionDescription('rootconfig', '', [od1, od2])
@ -498,3 +600,94 @@ def test_callback_hidden():
cfg.read_write() cfg.read_write()
raises(PropertiesOptionError, 'cfg.od1.opt1') raises(PropertiesOptionError, 'cfg.od1.opt1')
cfg.od2.opt2 cfg.od2.opt2
def test_callback_two_disabled():
opt1 = BoolOption('opt1', '', properties=('disabled',))
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',))
od1 = OptionDescription('od1', '', [opt1])
od2 = OptionDescription('od2', '', [opt2])
maconfig = OptionDescription('rootconfig', '', [od1, od2])
cfg = Config(maconfig)
cfg.read_write()
raises(PropertiesOptionError, 'cfg.od2.opt2')
def test_callback_calculating_disabled():
opt1 = BoolOption('opt1', '', properties=('disabled',))
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)})
od1 = OptionDescription('od1', '', [opt1])
od2 = OptionDescription('od2', '', [opt2])
maconfig = OptionDescription('rootconfig', '', [od1, od2])
cfg = Config(maconfig)
cfg.read_write()
raises(ConfigError, 'cfg.od2.opt2')
def test_callback_calculating_mandatory():
opt1 = BoolOption('opt1', '', properties=('disabled',))
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('mandatory',))
od1 = OptionDescription('od1', '', [opt1])
od2 = OptionDescription('od2', '', [opt2])
maconfig = OptionDescription('rootconfig', '', [od1, od2])
cfg = Config(maconfig)
cfg.read_only()
raises(ConfigError, 'cfg.od2.opt2')
def test_callback_two_disabled_multi():
opt1 = BoolOption('opt1', '', properties=('disabled',))
opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',), multi=True)
od1 = OptionDescription('od1', '', [opt1])
od2 = OptionDescription('od2', '', [opt2])
maconfig = OptionDescription('rootconfig', '', [od1, od2])
cfg = Config(maconfig)
cfg.read_write()
raises(PropertiesOptionError, 'cfg.od2.opt2')
def test_callback_multi_list_params():
val1 = StrOption('val1', "", multi=True, default=['val1', 'val2'])
val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'': ((val1, False),)})
oval2 = OptionDescription('val2', '', [val2])
maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', 'val', 'val']
def test_callback_multi_list_params_key():
val1 = StrOption('val1', "", multi=True, default=['val1', 'val2'])
val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'value': ((val1, False),)})
oval2 = OptionDescription('val2', '', [val2])
maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', 'val', 'val']
def test_callback_multi_multi():
val1 = StrOption('val1', "", multi=True, default=['val1', 'val2', 'val3'])
val2 = StrOption('val2', "", multi=True, default=['val11', 'val12'])
val3 = StrOption('val3', "", default='val4')
val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val2, False))})
val5 = StrOption('val5', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val3, False))})
val6 = StrOption('val6', "", multi=True, default=['val21', 'val22', 'val23'])
val7 = StrOption('val7', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val6, False))})
raises(ValueError, "StrOption('val8', '', multi=True, callback=return_list2, callback_params={'value': ((val1, False), (val6, False))})")
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5, val6, val7])
cfg = Config(maconfig)
cfg.read_write()
raises(ConfigError, "cfg.val4")
assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4']
assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23']
def test_multi_with_no_value():
#First option return [] (so without value)
val1 = StrOption('val1', "", ['val'], multi=True)
val2 = StrOption('val2', "", multi=True)
val3 = StrOption('val3', '', multi=True, callback=return_value, callback_params={'': ((val2, False),), 'value': ((val1, False),)})
od = OptionDescription('od', '', [val1, val2, val3])
c = Config(od)
raises(ConfigError, "c.val3")

View File

@ -4,7 +4,8 @@ from py.test import raises
from tiramisu.setting import owners, groups from tiramisu.setting import owners, groups
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
SymLinkOption, OptionDescription BroadcastOption, SymLinkOption, OptionDescription
from tiramisu.error import ConfigError
def test_consistency_not_equal(): def test_consistency_not_equal():
@ -22,6 +23,60 @@ def test_consistency_not_equal():
c.b = 2 c.b = 2
def test_consistency_not_equal_many_opts():
a = IntOption('a', '')
b = IntOption('b', '')
c = IntOption('c', '')
d = IntOption('d', '')
e = IntOption('e', '')
f = IntOption('f', '')
od = OptionDescription('od', '', [a, b, c, d, e, f])
a.impl_add_consistency('not_equal', b, c, d, e, f)
c = Config(od)
assert c.a is None
assert c.b is None
#
c.a = 1
del(c.a)
#
c.a = 1
raises(ValueError, "c.b = 1")
#
c.b = 2
raises(ValueError, "c.f = 2")
raises(ValueError, "c.f = 1")
#
c.d = 3
raises(ValueError, "c.f = 3")
raises(ValueError, "c.a = 3")
raises(ValueError, "c.c = 3")
raises(ValueError, "c.e = 3")
def test_consistency_not_in_config():
a = IntOption('a', '')
b = IntOption('b', '')
a.impl_add_consistency('not_equal', b)
od1 = OptionDescription('od1', '', [a])
od2 = OptionDescription('od2', '', [b])
od = OptionDescription('root', '', [od1])
raises(ConfigError, "Config(od)")
od = OptionDescription('root', '', [od1, od2])
Config(od)
#with subconfig
raises(ConfigError, "Config(od.od1)")
def test_consistency_afer_config():
a = IntOption('a', '')
b = IntOption('b', '')
od1 = OptionDescription('od1', '', [a])
od2 = OptionDescription('od2', '', [b])
od = OptionDescription('root', '', [od1, od2])
Config(od)
raises(AttributeError, "a.impl_add_consistency('not_equal', b)")
def test_consistency_not_equal_symlink(): def test_consistency_not_equal_symlink():
a = IntOption('a', '') a = IntOption('a', '')
b = IntOption('b', '') b = IntOption('b', '')
@ -29,7 +84,7 @@ def test_consistency_not_equal_symlink():
od = OptionDescription('od', '', [a, b, c]) od = OptionDescription('od', '', [a, b, c])
a.impl_add_consistency('not_equal', b) a.impl_add_consistency('not_equal', b)
c = Config(od) c = Config(od)
assert set(od._consistencies.keys()) == set([a, b]) assert set(od._cache_consistencies.keys()) == set([a, b])
def test_consistency_not_equal_multi(): def test_consistency_not_equal_multi():
@ -53,6 +108,14 @@ def test_consistency_default():
raises(ValueError, "a.impl_add_consistency('not_equal', b)") raises(ValueError, "a.impl_add_consistency('not_equal', b)")
def test_consistency_default_multi():
a = IntOption('a', '', [2, 1], multi=True)
b = IntOption('b', '', [1, 1], multi=True)
c = IntOption('c', '', [1, 2], multi=True)
raises(ValueError, "a.impl_add_consistency('not_equal', b)")
a.impl_add_consistency('not_equal', c)
def test_consistency_default_diff(): def test_consistency_default_diff():
a = IntOption('a', '', 3) a = IntOption('a', '', 3)
b = IntOption('b', '', 1) b = IntOption('b', '', 1)
@ -99,7 +162,7 @@ def test_consistency_ip_netmask_error_multi():
a = IPOption('a', '', multi=True) a = IPOption('a', '', multi=True)
b = NetmaskOption('b', '') b = NetmaskOption('b', '')
od = OptionDescription('od', '', [a, b]) od = OptionDescription('od', '', [a, b])
raises(ValueError, "b.impl_add_consistency('ip_netmask', a)") raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)")
def test_consistency_ip_netmask_multi(): def test_consistency_ip_netmask_multi():
@ -159,3 +222,53 @@ def test_consistency_network_netmask_multi_master():
c.a = ['192.168.1.0'] c.a = ['192.168.1.0']
c.b = ['255.255.255.0'] c.b = ['255.255.255.0']
raises(ValueError, "c.a = ['192.168.1.1']") raises(ValueError, "c.a = ['192.168.1.1']")
def test_consistency_broadcast():
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True)
c = BroadcastOption('c', '', multi=True)
od = OptionDescription('a', '', [a, b, c])
od.impl_set_group_type(groups.master)
b.impl_add_consistency('network_netmask', a)
c.impl_add_consistency('broadcast', a, b)
c = Config(od)
#first, test network_netmask
c.a = ['192.168.1.128']
raises(ValueError, "c.b = ['255.255.255.0']")
#
c.a = ['192.168.1.0']
c.b = ['255.255.255.0']
c.c = ['192.168.1.255']
raises(ValueError, "c.a = ['192.168.1.1']")
#
c.a = ['192.168.1.0', '192.168.2.128']
c.b = ['255.255.255.0', '255.255.255.128']
c.c = ['192.168.1.255', '192.168.2.255']
raises(ValueError, "c.c[1] = '192.168.2.128'")
c.c[1] = '192.168.2.255'
def test_consistency_broadcast_default():
a = NetworkOption('a', '', '192.168.1.0')
b = NetmaskOption('b', '', '255.255.255.128')
c = BroadcastOption('c', '', '192.168.2.127')
d = BroadcastOption('d', '', '192.168.1.127')
od = OptionDescription('a', '', [a, b, c])
raises(ValueError, "c.impl_add_consistency('broadcast', a, b)")
od2 = OptionDescription('a', '', [a, b, d])
d.impl_add_consistency('broadcast', a, b)
def test_consistency_not_all():
#_cache_consistencies is not None by not options has consistencies
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True)
c = BroadcastOption('c', '', multi=True)
od = OptionDescription('a', '', [a, b, c])
od.impl_set_group_type(groups.master)
b.impl_add_consistency('network_netmask', a)
c = Config(od)
c.a = ['192.168.1.0']
c.b = ['255.255.255.0']
c.c = ['192.168.1.255']

View File

@ -327,23 +327,23 @@ def test_reset_properties():
cfg = Config(descr) cfg = Config(descr)
setting = cfg.cfgimpl_get_settings() setting = cfg.cfgimpl_get_settings()
option = cfg.cfgimpl_get_description().gc.dummy option = cfg.cfgimpl_get_description().gc.dummy
assert setting._p_.get_properties(cfg) == {} assert setting._p_.get_modified_properties() == {}
setting.append('frozen') setting.append('frozen')
assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'cache', 'validator'))} assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'cache', 'validator'))}
setting.reset() setting.reset()
assert setting._p_.get_properties(cfg) == {} assert setting._p_.get_modified_properties() == {}
setting[option].append('test') setting[option].append('test')
assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))} assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))}
setting.reset() setting.reset()
assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))} assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))}
setting.append('frozen') setting.append('frozen')
assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
setting.reset(option) setting.reset(option)
assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache'))} assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache'))}
setting[option].append('test') setting[option].append('test')
assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
setting.reset(all_properties=True) setting.reset(all_properties=True)
assert setting._p_.get_properties(cfg) == {} assert setting._p_.get_modified_properties() == {}
raises(ValueError, 'setting.reset(all_properties=True, opt=option)') raises(ValueError, 'setting.reset(all_properties=True, opt=option)')
a = descr.wantref a = descr.wantref
setting[a].append('test') setting[a].append('test')

View File

@ -0,0 +1,155 @@
import autopath
import warnings
from py.test import raises
from tiramisu.config import Config
from tiramisu.option import StrOption, OptionDescription
from tiramisu.setting import groups
from tiramisu.error import ValueWarning
def return_true(value, param=None):
if value == 'val' and param in [None, 'yes']:
return True
def return_false(value, param=None):
if value == 'val' and param in [None, 'yes']:
raise ValueError('error')
def return_val(value, param=None):
return 'val'
def return_if_val(value):
if value != 'val':
raise ValueError('error')
def test_validator():
opt1 = StrOption('opt1', '', validator=return_true, default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')")
opt2 = StrOption('opt2', '', validator=return_false)
root = OptionDescription('root', '', [opt1, opt2])
cfg = Config(root)
assert cfg.opt1 == 'val'
raises(ValueError, "cfg.opt2 = 'val'")
def test_validator_params():
opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ('yes',)}, default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}, default='val')")
opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)})
root = OptionDescription('root', '', [opt1, opt2])
cfg = Config(root)
assert cfg.opt1 == 'val'
raises(ValueError, "cfg.opt2 = 'val'")
def test_validator_params_key():
opt1 = StrOption('opt1', '', validator=return_true, validator_params={'param': ('yes',)}, default='val')
raises(TypeError, "StrOption('opt2', '', validator=return_true, validator_params={'param_unknown': ('yes',)}, default='val')")
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
assert cfg.opt1 == 'val'
def test_validator_params_option():
opt0 = StrOption('opt0', '', default='val')
raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')")
def test_validator_multi():
opt1 = StrOption('opt1', '', validator=return_if_val, multi=True)
root = OptionDescription('root', '', [opt1])
cfg = Config(root)
assert cfg.opt1 == []
cfg.opt1.append('val')
assert cfg.opt1 == ['val']
raises(ValueError, "cfg.opt1.append('val1')")
raises(ValueError, "cfg.opt1 = ['val', 'val1']")
def test_validator_warning():
opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True)
opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True)
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True)
root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg = Config(root)
assert cfg.opt1 == 'val'
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.opt1 = 'val'
assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.opt2 = 'val'
assert len(w) == 1
assert w[0].message.opt == opt2
assert str(w[0].message) == 'invalid value val for option opt2: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.opt3.append('val')
assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.opt3.append('val1')
assert len(w) == 1
assert w[0].message.opt == opt3
assert str(w[0].message) == 'invalid value val1 for option opt3: error'
raises(ValueError, "cfg.opt2 = 1")
#
with warnings.catch_warnings(record=True) as w:
cfg.opt2 = 'val'
cfg.opt3.append('val')
assert len(w) == 2
assert w[0].message.opt == opt2
assert str(w[0].message) == 'invalid value val for option opt2: error'
assert w[1].message.opt == opt3
assert str(w[1].message) == 'invalid value val1 for option opt3: error'
def test_validator_warning_master_slave():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, warnings_only=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, warnings_only=True)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
assert interface1.impl_get_group_type() == groups.master
root = OptionDescription('root', '', [interface1])
cfg = Config(root)
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0.append(None)
assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1']
assert len(w) == 1
assert w[0].message.opt == netmask_admin_eth0
assert str(w[0].message) == 'invalid value val1 for option netmask_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val']
assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1']
assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1']
assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
warnings.resetwarnings()
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val']
assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'

View File

@ -64,9 +64,9 @@ def test_make_dict_filter():
config = Config(descr) config = Config(descr)
config.read_write() config.read_write()
subresult = {'numero_etab': None, 'nombre_interfaces': 1, subresult = {'numero_etab': None, 'nombre_interfaces': 1,
'serveur_ntp': [], 'mode_conteneur_actif': False, 'serveur_ntp': [], 'mode_conteneur_actif': False,
'time_zone': 'Paris', 'nom_machine': 'eoleng', 'time_zone': 'Paris', 'nom_machine': 'eoleng',
'activer_proxy_client': False} 'activer_proxy_client': False}
result = {} result = {}
for key, value in subresult.items(): for key, value in subresult.items():
result['general.' + key] = value result['general.' + key] = value
@ -114,7 +114,6 @@ def test_iter_not_group():
raises(TypeError, "list(config.iter_groups(group_type='family'))") raises(TypeError, "list(config.iter_groups(group_type='family'))")
def test_groups_with_master(): def test_groups_with_master():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=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) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
@ -252,6 +251,22 @@ def test_values_with_master_and_slaves_master():
assert cfg.ip_admin_eth0.netmask_admin_eth0 == [] assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
def test_values_with_master_and_slaves_master_error():
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 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('toto', '', [interface1])
cfg = Config(maconfig)
cfg.read_write()
cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"]
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']")
cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0']
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']")
raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']")
def test_values_with_master_owner(): def test_values_with_master_owner():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=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) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)

View File

@ -1,8 +1,16 @@
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
OptionDescription OptionDescription
from tiramisu.config import Config
from tiramisu.setting import owners
from tiramisu.storage import delete_session
from tiramisu.error import ConfigError
from pickle import dumps, loads from pickle import dumps, loads
def return_value(value=None):
return value
def _get_slots(opt): def _get_slots(opt):
slots = set() slots = set()
for subclass in opt.__class__.__mro__: for subclass in opt.__class__.__mro__:
@ -32,7 +40,7 @@ def _diff_opt(opt1, opt2):
if diff2 != set(): if diff2 != set():
raise Exception('more attribute in opt2 {0}'.format(list(diff2))) raise Exception('more attribute in opt2 {0}'.format(list(diff2)))
for attr in attr1: for attr in attr1:
if attr in ['_cache_paths']: if attr in ['_cache_paths', '_cache_consistencies']:
continue continue
err1 = False err1 = False
err2 = False err2 = False
@ -64,7 +72,20 @@ def _diff_opt(opt1, opt2):
if isinstance(val1, list): if isinstance(val1, list):
for index, consistency in enumerate(val1): for index, consistency in enumerate(val1):
assert consistency[0] == val2[index][0] assert consistency[0] == val2[index][0]
assert consistency[1]._name == val2[index][1]._name for idx, opt in enumerate(consistency[1]):
assert opt._name == val2[index][1][idx]._name
elif attr == '_callback':
assert val1[0] == val2[0]
if val1[1] is not None:
for key, values in val1[1].items():
for idx, value in enumerate(values):
if isinstance(value, tuple):
assert val1[1][key][idx][0]._name == val2[1][key][idx][0]._name
assert val1[1][key][idx][1] == val2[1][key][idx][1]
else:
assert val1[1][key][idx] == val2[1][key][idx]
else:
assert val1[1] == val2[1]
else: else:
assert val1 == val2 assert val1 == val2
@ -104,6 +125,23 @@ def test_diff_opt_cache():
_diff_opt(o1.o.s, q.o.s) _diff_opt(o1.o.s, q.o.s)
def test_diff_opt_callback():
b = BoolOption('b', '', callback=return_value)
b2 = BoolOption('b2', '', callback=return_value, callback_params={'': ('yes',)})
b3 = BoolOption('b3', '', callback=return_value, callback_params={'': ('yes', (b, False)), 'value': ('no',)})
o = OptionDescription('o', '', [b, b2, b3])
o1 = OptionDescription('o1', '', [o])
o1.impl_build_cache()
a = dumps(o1)
q = loads(a)
_diff_opt(o1, q)
_diff_opt(o1.o, q.o)
_diff_opt(o1.o.b, q.o.b)
_diff_opt(o1.o.b2, q.o.b2)
_diff_opt(o1.o.b3, q.o.b3)
def test_no_state_attr(): def test_no_state_attr():
# all _state_xxx attributes should be deleted # all _state_xxx attributes should be deleted
b = BoolOption('b', '') b = BoolOption('b', '')
@ -119,3 +157,95 @@ def test_no_state_attr():
_no_state(q.o.b) _no_state(q.o.b)
_no_state(q.o.u) _no_state(q.o.u)
_no_state(q.o.s) _no_state(q.o.s)
def test_state_config():
val1 = BoolOption('val1', "")
maconfig = OptionDescription('rootconfig', '', [val1])
try:
cfg = Config(maconfig, persistent=True, session_id='29090931')
except ValueError:
cfg = Config(maconfig, session_id='29090931')
cfg._impl_test = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
try:
delete_session('29090931')
except ConfigError:
pass
def test_state_properties():
val1 = BoolOption('val1', "")
maconfig = OptionDescription('rootconfig', '', [val1])
try:
cfg = Config(maconfig, persistent=True, session_id='29090932')
except ValueError:
cfg = Config(maconfig, session_id='29090932')
cfg._impl_test = True
cfg.read_write()
cfg.cfgimpl_get_settings()[val1].append('test')
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
try:
delete_session('29090931')
except ConfigError:
pass
def test_state_values():
val1 = BoolOption('val1', "")
maconfig = OptionDescription('rootconfig', '', [val1])
try:
cfg = Config(maconfig, persistent=True, session_id='29090933')
except ValueError:
cfg = Config(maconfig, session_id='29090933')
cfg._impl_test = True
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
q.val1 = False
#assert cfg.val1 is True
assert q.val1 is False
try:
delete_session('29090931')
except ConfigError:
pass
def test_state_values_owner():
val1 = BoolOption('val1', "")
maconfig = OptionDescription('rootconfig', '', [val1])
try:
cfg = Config(maconfig, persistent=True, session_id='29090934')
except ValueError:
cfg = Config(maconfig, session_id='29090934')
cfg._impl_test = True
owners.addowner('newowner')
cfg.cfgimpl_get_settings().setowner(owners.newowner)
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
q.val1 = False
nval1 = q.cfgimpl_get_description().val1
assert q.getowner(nval1) == owners.newowner
try:
delete_session('29090931')
except ConfigError:
pass

View File

@ -23,11 +23,9 @@ from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.i18n import _ from tiramisu.i18n import _
# ____________________________________________________________ # ____________________________________________________________
def carry_out_calculation(name,
config, def carry_out_calculation(name, config, callback, callback_params,
callback, index=None, max_len=None):
callback_params,
index=None):
"""a function that carries out a calculation for an option's value """a function that carries out a calculation for an option's value
:param name: the option name (`opt._name`) :param name: the option name (`opt._name`)
@ -40,73 +38,161 @@ def carry_out_calculation(name,
:type callback_params: dict :type callback_params: dict
:param index: if an option is multi, only calculates the nth value :param index: if an option is multi, only calculates the nth value
:type index: int :type index: int
""" :param max_len: max length for a multi
#callback, callback_params = option.getcallback() :type max_len: int
#if callback_params is None:
# callback_params = {}
tcparams = {}
one_is_multi = False
len_multi = 0
for key, values in callback_params.items(): The callback_params is a dict. Key is used to build args (if key is '')
for value in values: and kwargs (otherwise). Values are tuple of:
if type(value) == tuple: - values
path, check_disabled = value - tuple with option and boolean's force_permissive (True when don't raise
if config is None: if PropertiesOptionError)
if check_disabled: Values could have multiple values only when key is ''.
continue
raise ConfigError(_('no config specified but needed')) * if no callback_params:
=> calculate()
* if callback_params={'': ('yes',)}
=> calculate('yes')
* if callback_params={'value': ('yes',)}
=> calculate(value='yes')
* if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no')
* if callback_params={'value': ('yes', 'no')}
=> ValueError()
* if callback_params={'': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(11)
- a multi option:
opt1 == [1, 2, 3]
=> calculate(1)
=> calculate(2)
=> calculate(3)
* if callback_params={'value': ((opt1, False),)}
- a simple option:
opt1 == 11
=> calculate(value=11)
- a multi option:
opt1 == [1, 2, 3]
=> calculate(value=1)
=> calculate(value=2)
=> calculate(value=3)
* if callback_params={'': ((opt1, False), (opt2, False))}
- a multi option with a simple option
opt1 == [1, 2, 3]
opt2 == 11
=> calculate(1, 11)
=> calculate(2, 11)
=> calculate(3, 11)
- a multi option with an other multi option but with same length
opt1 == [1, 2, 3]
opt2 == [11, 12, 13]
=> calculate(1, 11)
=> calculate(2, 12)
=> calculate(3, 13)
- a multi option with an other multi option but with different length
opt1 == [1, 2, 3]
opt2 == [11, 12]
=> ConfigError()
- a multi option without value with a simple option
opt1 == []
opt2 == 11
=> []
* if callback_params={'value': ((opt1, False), (opt2, False))}
=> ConfigError()
If index is not None, return a value, otherwise return:
* a list if one parameters have multi option
* a value otherwise
If calculate return list, this list is extend to return value.
"""
tcparams = {}
# if callback_params has a callback, launch several time calculate()
one_is_multi = False
# multi's option should have same value for all option
len_multi = None
for key, callbacks in callback_params.items():
for callbk in callbacks:
if isinstance(callbk, tuple):
# callbk is something link (opt, True|False)
option, force_permissive = callbk
path = config.cfgimpl_get_description().impl_get_path_by_opt(
option)
# get value
try: try:
opt_value = config._getattr(path, force_permissive=True) value = config._getattr(path, force_permissive=True)
opt = config.unwrap_from_path(path, force_permissive=True)
except PropertiesOptionError as err: except PropertiesOptionError as err:
if check_disabled: if force_permissive:
continue continue
raise ConfigError(_('unable to carry out a calculation, ' raise ConfigError(_('unable to carry out a calculation, '
'option {0} has properties: {1} for: ' 'option {0} has properties: {1} for: '
'{2}').format(path, err.proptype, '{2}').format(option._name,
err.proptype,
name)) name))
is_multi = opt.impl_is_multi() is_multi = option.impl_is_multi()
if is_multi: if is_multi:
if opt_value is not None: len_value = len(value)
len_value = len(opt_value) if len_multi is not None and len_multi != len_value:
if len_multi != 0 and len_multi != len_value: raise ConfigError(_('unable to carry out a '
raise ConfigError(_('unable to carry out a ' 'calculation, option value with'
'calculation, option value with' ' multi types must have same '
' multi types must have same ' 'length for: {0}').format(name))
'length for: {0}').format(name)) len_multi = len_value
len_multi = len_value
one_is_multi = True one_is_multi = True
tcparams.setdefault(key, []).append((opt_value, is_multi)) tcparams.setdefault(key, []).append((value, is_multi))
else: else:
tcparams.setdefault(key, []).append((value, False)) # callbk is a value and not a multi
tcparams.setdefault(key, []).append((callbk, False))
# if one value is a multi, launch several time calculate
# if index is set, return a value
# if no index, return a list
if one_is_multi: if one_is_multi:
ret = [] ret = []
if index: if index:
range_ = [index] if index < len_multi:
range_ = [index]
else:
range_ = []
ret = None
else: else:
range_ = range(len_multi) if max_len and max_len < len_multi:
range_ = range(max_len)
else:
range_ = range(len_multi)
for incr in range_: for incr in range_:
tcp = {} args = []
params = [] kwargs = {}
for key, couples in tcparams.items(): for key, couples in tcparams.items():
for couple in couples: for couple in couples:
value, ismulti = couple value, ismulti = couple
if ismulti and value is not None: if ismulti:
if key == '': val = value[incr]
params.append(value[incr])
else:
if len(value) > incr:
tcp[key] = value[incr]
else:
tcp[key] = ''
else: else:
if key == '': val = value
params.append(value) if key == '':
else: args.append(val)
tcp[key] = value else:
calc = calculate(name, callback, params, tcp) kwargs[key] = val
calc = calculate(callback, args, kwargs)
if index: if index:
ret = calc ret = calc
else: else:
@ -114,28 +200,28 @@ def carry_out_calculation(name,
ret.extend(calc) ret.extend(calc)
else: else:
ret.append(calc) ret.append(calc)
return ret return ret
else: else:
tcp = {} # no value is multi
params = [] # return a single value
args = []
kwargs = {}
for key, couples in tcparams.items(): for key, couples in tcparams.items():
for couple in couples: for couple in couples:
# couple[1] (ismulti) is always False
if key == '': if key == '':
value = couple[0] args.append(couple[0])
params.append(value)
else: else:
tcp[key] = couple[0] kwargs[key] = couple[0]
return calculate(name, callback, params, tcp) return calculate(callback, args, kwargs)
def calculate(name, callback, params, tcparams): def calculate(callback, args, kwargs):
# FIXME we don't need the option's name down there.
"""wrapper that launches the 'callback' """wrapper that launches the 'callback'
:param callback: callback name :param callback: callback function
:param params: in the callback's arity, the unnamed parameters :param args: in the callback's arity, the unnamed parameters
:param tcparams: in the callback's arity, the named parameters :param kwargs: in the callback's arity, the named parameters
""" """
return callback(*params, **tcparams) return callback(*args, **kwargs)

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"options handler global entry point"
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -20,17 +19,23 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
"options handler global entry point"
import weakref import weakref
from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.option import OptionDescription, Option, SymLinkOption
from tiramisu.setting import groups, Settings, default_encoding from tiramisu.setting import groups, Settings, default_encoding
from tiramisu.storage import get_storages from tiramisu.storage import get_storages, get_storage, set_storage, \
from tiramisu.value import Values _impl_getstate_setting
from tiramisu.value import Values, Multi
from tiramisu.i18n import _ from tiramisu.i18n import _
class SubConfig(object): class SubConfig(object):
"sub configuration management entry" """Sub configuration management entry.
Tree if OptionDescription's responsability. SubConfig are generated
on-demand. A Config is also a SubConfig.
Root Config is call context below
"""
__slots__ = ('_impl_context', '_impl_descr', '_impl_path') __slots__ = ('_impl_context', '_impl_descr', '_impl_path')
def __init__(self, descr, context, subpath=None): def __init__(self, descr, context, subpath=None):
@ -55,6 +60,7 @@ class SubConfig(object):
def cfgimpl_reset_cache(self, only_expired=False, only=('values', def cfgimpl_reset_cache(self, only_expired=False, only=('values',
'settings')): 'settings')):
"remove cache (in context)"
self._cfgimpl_get_context().cfgimpl_reset_cache(only_expired, only) self._cfgimpl_get_context().cfgimpl_reset_cache(only_expired, only)
def cfgimpl_get_home_by_path(self, path, force_permissive=False, def cfgimpl_get_home_by_path(self, path, force_permissive=False,
@ -179,7 +185,9 @@ class SubConfig(object):
homeconfig, name = self.cfgimpl_get_home_by_path(name) homeconfig, name = self.cfgimpl_get_home_by_path(name)
return homeconfig.__setattr__(name, value) return homeconfig.__setattr__(name, value)
child = getattr(self.cfgimpl_get_description(), name) child = getattr(self.cfgimpl_get_description(), name)
if not isinstance(child, SymLinkOption): if isinstance(child, OptionDescription):
raise TypeError(_("can't assign to an OptionDescription"))
elif not isinstance(child, SymLinkOption):
if self._impl_path is None: if self._impl_path is None:
path = name path = name
else: else:
@ -293,12 +301,13 @@ class SubConfig(object):
return True return True
try: try:
value = getattr(self, path) value = getattr(self, path)
if value == byvalue: if isinstance(value, Multi):
return True return byvalue in value
else:
return value == byvalue
except PropertiesOptionError: # a property is a restriction except PropertiesOptionError: # a property is a restriction
# upon the access of the value # upon the access of the value
pass return False
return False
def _filter_by_type(): def _filter_by_type():
if bytype is None: if bytype is None:
@ -323,15 +332,15 @@ class SubConfig(object):
continue continue
if not _filter_by_value(): if not _filter_by_value():
continue continue
if not _filter_by_type():
continue
#remove option with propertyerror, ... #remove option with propertyerror, ...
if check_properties: if byvalue is None and check_properties:
try: try:
value = getattr(self, path) value = getattr(self, path)
except PropertiesOptionError: except PropertiesOptionError:
# a property restricts the access of the value # a property restricts the access of the value
continue continue
if not _filter_by_type():
continue
if type_ == 'value': if type_ == 'value':
retval = value retval = value
elif type_ == 'path': elif type_ == 'path':
@ -521,7 +530,7 @@ class CommonConfig(SubConfig):
# ____________________________________________________________ # ____________________________________________________________
class Config(CommonConfig): class Config(CommonConfig):
"main configuration management entry" "main configuration management entry"
__slots__ = ('__weakref__', ) __slots__ = ('__weakref__', '_impl_test')
def __init__(self, descr, session_id=None, persistent=False): def __init__(self, descr, session_id=None, persistent=False):
""" Configuration option management master class """ Configuration option management master class
@ -542,6 +551,43 @@ class Config(CommonConfig):
super(Config, self).__init__(descr, weakref.ref(self)) super(Config, self).__init__(descr, weakref.ref(self))
self._impl_build_all_paths() self._impl_build_all_paths()
self._impl_meta = None self._impl_meta = None
#undocumented option used only in test script
self._impl_test = False
def __getstate__(self):
if self._impl_meta is not None:
raise ConfigError('cannot serialize Config with meta')
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
slots -= frozenset(['_impl_context', '__weakref__'])
state = {}
for slot in slots:
try:
state[slot] = getattr(self, slot)
except AttributeError:
pass
storage = self._impl_values._p_._storage
if not storage.serializable:
raise ConfigError('this storage is not serialisable, could be a '
'none persistent storage')
state['_storage'] = {'session_id': storage.session_id,
'persistent': storage.persistent}
state['_impl_setting'] = _impl_getstate_setting()
return state
def __setstate__(self, state):
for key, value in state.items():
if key not in ['_storage', '_impl_setting']:
setattr(self, key, value)
set_storage(**state['_impl_setting'])
self._impl_context = weakref.ref(self)
self._impl_settings.context = weakref.ref(self)
self._impl_values.context = weakref.ref(self)
storage = get_storage(test=self._impl_test, **state['_storage'])
self._impl_values._impl_setstate(storage)
self._impl_settings._impl_setstate(storage)
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, only_expired=False,

View File

@ -61,3 +61,35 @@ class SlaveError(Exception):
class ConstError(TypeError): class ConstError(TypeError):
"no uniq value in _NameSpace" "no uniq value in _NameSpace"
pass pass
#Warning
class ValueWarning(UserWarning):
"""Option could warn user and not raise ValueError.
Example:
>>> import warnings
>>> from tiramisu.error import ValueWarning
>>> from tiramisu.option import StrOption, OptionDescription
>>> from tiramisu.config import Config
>>> warnings.simplefilter("always", ValueWarning)
>>> def a(val):
... raise ValueError('pouet')
...
>>> s=StrOption('s', '', validator=a, warnings_only=True)
>>> o=OptionDescription('o', '', [s])
>>> c=Config(o)
>>> c.s = 'val'
StrOption:0: ValueWarning: invalid value val for option s: pouet
>>> with warnings.catch_warnings(record=True) as w:
... c.s = 'val'
...
>>> w[0].message.opt == s
True
>>> print str(w[0].message)
invalid value val for option s: pouet
"""
def __init__(self, msg, opt):
self.opt = opt
super(ValueWarning, self).__init__(msg)

View File

@ -25,8 +25,9 @@ import sys
from copy import copy, deepcopy from copy import copy, deepcopy
from types import FunctionType from types import FunctionType
from IPy import IP from IPy import IP
import warnings
from tiramisu.error import ConflictError from tiramisu.error import ConfigError, ConflictError, ValueWarning
from tiramisu.setting import groups, multitypes from tiramisu.setting import groups, multitypes
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
@ -60,9 +61,8 @@ class BaseOption(object):
__setattr__ method __setattr__ method
""" """
__slots__ = ('_name', '_requires', '_properties', '_readonly', __slots__ = ('_name', '_requires', '_properties', '_readonly',
'_consistencies', '_calc_properties', '_impl_informations', '_calc_properties', '_impl_informations',
'_state_consistencies', '_state_readonly', '_state_requires', '_state_readonly', '_state_requires', '_stated')
'_stated')
def __init__(self, name, doc, requires, properties): def __init__(self, name, doc, requires, properties):
if not valid_name(name): if not valid_name(name):
@ -72,7 +72,6 @@ class BaseOption(object):
self.impl_set_information('doc', doc) self.impl_set_information('doc', doc)
self._calc_properties, self._requires = validate_requires_arg( self._calc_properties, self._requires = validate_requires_arg(
requires, self._name) requires, self._name)
self._consistencies = None
if properties is None: if properties is None:
properties = tuple() properties = tuple()
if not isinstance(properties, tuple): if not isinstance(properties, tuple):
@ -97,8 +96,7 @@ class BaseOption(object):
"frozen" (which has noting to do with the high level "freeze" "frozen" (which has noting to do with the high level "freeze"
propertie or "read_only" property) propertie or "read_only" property)
""" """
if not name.startswith('_state') and \ if not name.startswith('_state') and not name.startswith('_cache'):
name not in ('_cache_paths', '_consistencies'):
is_readonly = False is_readonly = False
# never change _name # never change _name
if name == '_name': if name == '_name':
@ -108,15 +106,12 @@ class BaseOption(object):
is_readonly = True is_readonly = True
except: except:
pass pass
try: elif name != '_readonly':
if self._readonly is True: try:
if value is True: if self._readonly is True:
# already readonly and try to re set readonly is_readonly = True
# don't raise, just exit except AttributeError:
return self._readonly = False
is_readonly = True
except AttributeError:
pass
if is_readonly: if is_readonly:
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format( " read-only").format(
@ -148,56 +143,6 @@ class BaseOption(object):
raise ValueError(_("information's item not found: {0}").format( raise ValueError(_("information's item not found: {0}").format(
key)) key))
# serialize/unserialize
def _impl_convert_consistencies(self, descr, load=False):
"""during serialization process, many things have to be done.
one of them is the localisation of the options.
The paths are set once for all.
:type descr: :class:`tiramisu.option.OptionDescription`
:param load: `True` if we are at the init of the option description
:type load: bool
"""
if not load and self._consistencies is None:
self._state_consistencies = None
elif load and self._state_consistencies is None:
self._consistencies = None
del(self._state_consistencies)
else:
if load:
consistencies = self._state_consistencies
else:
consistencies = self._consistencies
if isinstance(consistencies, list):
new_value = []
for consistency in consistencies:
if load:
new_value.append((consistency[0],
descr.impl_get_opt_by_path(
consistency[1])))
else:
new_value.append((consistency[0],
descr.impl_get_path_by_opt(
consistency[1])))
else:
new_value = {}
for key, _consistencies in consistencies.items():
new_value[key] = []
for key_cons, _cons in _consistencies:
_list_cons = []
for _con in _cons:
if load:
_list_cons.append(descr.impl_get_opt_by_path(_con))
else:
_list_cons.append(descr.impl_get_path_by_opt(_con))
new_value[key].append((key_cons, tuple(_list_cons)))
if load:
del(self._state_consistencies)
self._consistencies = new_value
else:
self._state_consistencies = new_value
def _impl_convert_requires(self, descr, load=False): def _impl_convert_requires(self, descr, load=False):
"""export of the requires during the serialization process """export of the requires during the serialization process
@ -240,12 +185,10 @@ class BaseOption(object):
:param descr: the parent :class:`tiramisu.option.OptionDescription` :param descr: the parent :class:`tiramisu.option.OptionDescription`
""" """
self._stated = True self._stated = True
self._impl_convert_consistencies(descr) for func in dir(self):
self._impl_convert_requires(descr) if func.startswith('_impl_convert_'):
try: getattr(self, func)(descr)
self._state_readonly = self._readonly self._state_readonly = self._readonly
except AttributeError:
pass
def __getstate__(self, stated=True): def __getstate__(self, stated=True):
"""special method to enable the serialization with pickle """special method to enable the serialization with pickle
@ -265,7 +208,8 @@ class BaseOption(object):
for subclass in self.__class__.__mro__: for subclass in self.__class__.__mro__:
if subclass is not object: if subclass is not object:
slots.update(subclass.__slots__) slots.update(subclass.__slots__)
slots -= frozenset(['_cache_paths', '__weakref__']) slots -= frozenset(['_cache_paths', '_cache_consistencies',
'__weakref__'])
states = {} states = {}
for slot in slots: for slot in slots:
# remove variable if save variable converted # remove variable if save variable converted
@ -292,8 +236,9 @@ class BaseOption(object):
:type descr: :class:`tiramisu.option.OptionDescription` :type descr: :class:`tiramisu.option.OptionDescription`
""" """
self._impl_convert_consistencies(descr, load=True) for func in dir(self):
self._impl_convert_requires(descr, load=True) if func.startswith('_impl_convert_'):
getattr(self, func)(descr, load=True)
try: try:
self._readonly = self._state_readonly self._readonly = self._state_readonly
del(self._state_readonly) del(self._state_readonly)
@ -322,13 +267,15 @@ class Option(BaseOption):
Reminder: an Option object is **not** a container for the value. Reminder: an Option object is **not** a container for the value.
""" """
__slots__ = ('_multi', '_validator', '_default_multi', '_default', __slots__ = ('_multi', '_validator', '_default_multi', '_default',
'_callback', '_multitype', '_master_slaves', '__weakref__') '_state_callback', '_callback', '_multitype',
'_consistencies', '_warnings_only', '_master_slaves',
'_state_consistencies', '__weakref__')
_empty = '' _empty = ''
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None, callback_params=None, validator=None, validator_params=None,
properties=None): properties=None, warnings_only=False):
""" """
:param name: the option's name :param name: the option's name
:param doc: the option's description :param doc: the option's description
@ -344,18 +291,17 @@ class Option(BaseOption):
:param callback_params: the callback's parameter :param callback_params: the callback's parameter
:param validator: the name of a function which stands for a custom :param validator: the name of a function which stands for a custom
validation of the value validation of the value
:param validator_args: the validator's parameters :param validator_params: the validator's parameters
:param properties: tuple of default properties :param properties: tuple of default properties
:param warnings_only: _validator and _consistencies don't raise if True
Values()._warning contain message
""" """
super(Option, self).__init__(name, doc, requires, properties) super(Option, self).__init__(name, doc, requires, properties)
self._multi = multi self._multi = multi
if validator is not None: if validator is not None:
if type(validator) != FunctionType: validate_callback(validator, validator_params, 'validator')
raise TypeError(_("validator must be a function")) self._validator = (validator, validator_params)
if validator_args is None:
validator_args = {}
self._validator = (validator, validator_args)
else: else:
self._validator = None self._validator = None
if not self._multi and default_multi is not None: if not self._multi and default_multi is not None:
@ -377,11 +323,7 @@ class Option(BaseOption):
"no callback defined" "no callback defined"
" yet for option {0}").format(name)) " yet for option {0}").format(name))
if callback is not None: if callback is not None:
if type(callback) != FunctionType: validate_callback(callback, callback_params, 'callback')
raise ValueError('callback must be a function')
if callback_params is not None and \
not isinstance(callback_params, dict):
raise ValueError('callback_params must be a dict')
self._callback = (callback, callback_params) self._callback = (callback, callback_params)
else: else:
self._callback = None self._callback = None
@ -390,101 +332,124 @@ class Option(BaseOption):
default = [] default = []
self._multitype = multitypes.default self._multitype = multitypes.default
self._default_multi = default_multi self._default_multi = default_multi
self._warnings_only = warnings_only
self.impl_validate(default) self.impl_validate(default)
self._default = default self._default = default
self._consistencies = None
def _launch_consistency(self, func, opt, vals, context, index, opt_): def _launch_consistency(self, func, option, value, context, index,
all_cons_opts):
"""Launch consistency now
:param func: function name, this name should start with _cons_
:type func: `str`
:param option: option that value is changing
:type option: `tiramisu.option.Option`
:param value: new value of this option
:param context: Config's context, if None, check default value instead
:type context: `tiramisu.config.Config`
:param index: only for multi option, consistency should be launch for
specified index
:type index: `int`
:param all_cons_opts: all options concerne by this consistency
:type all_cons_opts: `list` of `tiramisu.option.Option`
"""
if context is not None: if context is not None:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
if opt is self: #option is also in all_cons_opts
#values are for self, search opt_ values if option not in all_cons_opts:
values = vals raise ConfigError(_('option not in all_cons_opts'))
if context is not None:
path = descr.impl_get_path_by_opt(opt_)
values_ = context._getattr(path, validate=False)
else:
values_ = opt_.impl_getdefault()
if index is not None:
#value is not already set, could be higher
try:
values_ = values_[index]
except IndexError:
values_ = None
else:
#values are for opt_, search self values
values_ = vals
if context is not None:
path = descr.impl_get_path_by_opt(self)
values = context._getattr(path, validate=False)
else:
values = self.impl_getdefault()
if index is not None:
#value is not already set, could be higher
try:
values = values[index]
except IndexError:
values = None
if index is None and self.impl_is_multi():
for index in range(0, len(values)):
try:
value = values[index]
value_ = values_[index]
except IndexError:
value = None
value_ = None
if None not in (value, value_):
getattr(self, func)(opt_._name, value, value_)
else:
if None not in (values, values_):
getattr(self, func)(opt_._name, values, values_)
def impl_validate(self, value, context=None, validate=True): all_cons_vals = []
for opt in all_cons_opts:
#get value
if option == opt:
opt_value = value
else:
#if context, calculate value, otherwise get default value
if context is not None:
opt_value = context._getattr(
descr.impl_get_path_by_opt(opt), validate=False)
else:
opt_value = opt.impl_getdefault()
#append value
if not self.impl_is_multi() or option == opt:
all_cons_vals.append(opt_value)
else:
#value is not already set, could be higher index
try:
all_cons_vals.append(opt_value[index])
except IndexError:
#so return if no value
return
getattr(self, func)(all_cons_opts, all_cons_vals)
def impl_validate(self, value, context=None, validate=True,
force_index=None):
""" """
:param value: the option's value :param value: the option's value
:param context: Config's context
:type context: :class:`tiramisu.config.Config`
:param validate: if true enables ``self._validator`` validation :param validate: if true enables ``self._validator`` validation
:type validate: boolean
:param force_no_multi: if multi, value has to be a list
not if force_no_multi is True
:type force_no_multi: boolean
""" """
if not validate: if not validate:
return return
def val_validator(val): def val_validator(val):
if self._validator is not None: if self._validator is not None:
callback_params = deepcopy(self._validator[1]) if self._validator[1] is not None:
callback_params.setdefault('', []).insert(0, val) validator_params = deepcopy(self._validator[1])
return carry_out_calculation(self._name, config=context, if '' in validator_params:
callback=self._validator[0], lst = list(validator_params[''])
callback_params=callback_params) lst.insert(0, val)
else: validator_params[''] = tuple(lst)
return True else:
validator_params[''] = (val,)
else:
validator_params = {'': (val,)}
# Raise ValueError if not valid
carry_out_calculation(self._name, config=context,
callback=self._validator[0],
callback_params=validator_params)
def do_validation(_value, _index=None): def do_validation(_value, _index=None):
if _value is None: if _value is None:
return True return
if not val_validator(_value): # option validation
raise ValueError(_("invalid value {0} " self._validate(_value)
"for option {1} for object {2}"
).format(_value,
self._name,
self.__class__.__name__))
try: try:
self._validate(_value) # valid with self._validator
val_validator(_value)
# if not context launch consistency validation
if context is not None:
descr._valid_consistency(self, _value, context, _index)
self._second_level_validation(_value)
except ValueError as err: except ValueError as err:
raise ValueError(_("invalid value {0} for option {1}: {2}" msg = _("invalid value {0} for option {1}: {2}").format(
"").format(_value, self._name, err)) _value, self._name, err)
if context is not None: if self._warnings_only:
descr._valid_consistency(self, _value, context, _index) warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
else:
raise ValueError(msg)
# generic calculation # generic calculation
if context is not None: if context is not None:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
if not self._multi:
do_validation(value) if not self._multi or force_index is not None:
do_validation(value, force_index)
else: else:
if not isinstance(value, list): if not isinstance(value, list):
raise ValueError(_("invalid value {0} for option {1} " raise ValueError(_("which must be a list").format(value,
"which must be a list").format(value,
self._name)) self._name))
for index in range(0, len(value)): for index, val in enumerate(value):
val = value[index]
do_validation(val, index) do_validation(val, index)
def impl_getdefault(self, default_multi=False): def impl_getdefault(self, default_multi=False):
@ -529,29 +494,133 @@ class Option(BaseOption):
def impl_is_multi(self): def impl_is_multi(self):
return self._multi return self._multi
def impl_add_consistency(self, func, opt): def impl_add_consistency(self, func, *other_opts):
"""Add consistency means that value will be validate with other_opts
option's values.
:param func: function's name
:type func: `str`
:param other_opts: options used to validate value
:type other_opts: `list` of `tiramisu.option.Option`
"""
if self._consistencies is None: if self._consistencies is None:
self._consistencies = [] self._consistencies = []
if not isinstance(opt, Option): for opt in other_opts:
raise ValueError('consistency must be set with an option') if not isinstance(opt, Option):
if self is opt: raise ConfigError(_('consistency should be set with an option'))
raise ValueError('cannot add consistency with itself') if self is opt:
if self.impl_is_multi() != opt.impl_is_multi(): raise ConfigError(_('cannot add consistency with itself'))
raise ValueError('options in consistency' if self.impl_is_multi() != opt.impl_is_multi():
' should be multi in two sides') raise ConfigError(_('every options in consistency should be '
'multi or none'))
func = '_cons_{0}'.format(func) func = '_cons_{0}'.format(func)
self._launch_consistency(func, all_cons_opts = tuple([self] + list(other_opts))
self, value = self.impl_getdefault()
self.impl_getdefault(), if value is not None:
None, None, opt) if self.impl_is_multi():
self._consistencies.append((func, opt)) for idx, val in enumerate(value):
self._launch_consistency(func, self, val, None,
idx, all_cons_opts)
else:
self._launch_consistency(func, self, value, None,
None, all_cons_opts)
self._consistencies.append((func, all_cons_opts))
self.impl_validate(self.impl_getdefault()) self.impl_validate(self.impl_getdefault())
def _cons_not_equal(self, optname, value, value_): def _cons_not_equal(self, opts, vals):
if value == value_: for idx_inf, val_inf in enumerate(vals):
raise ValueError(_("invalid value {0} for option {1} " for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
"must be different as {2} option" if val_inf == val_sup is not None:
"").format(value, self._name, optname)) raise ValueError(_("same value for {0} and {1}").format(
opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
def _impl_convert_callbacks(self, descr, load=False):
if not load and self._callback is None:
self._state_callback = None
elif load and self._state_callback is None:
self._callback = None
del(self._state_callback)
else:
if load:
callback, callback_params = self._state_callback
else:
callback, callback_params = self._callback
if callback_params is not None:
cllbck_prms = {}
for key, values in callback_params.items():
vls = []
for value in values:
if isinstance(value, tuple):
if load:
value = (descr.impl_get_opt_by_path(value[0]),
value[1])
else:
value = (descr.impl_get_path_by_opt(value[0]),
value[1])
vls.append(value)
cllbck_prms[key] = tuple(vls)
else:
cllbck_prms = None
if load:
del(self._state_callback)
self._callback = (callback, cllbck_prms)
else:
self._state_callback = (callback, cllbck_prms)
# serialize/unserialize
def _impl_convert_consistencies(self, descr, load=False):
"""during serialization process, many things have to be done.
one of them is the localisation of the options.
The paths are set once for all.
:type descr: :class:`tiramisu.option.OptionDescription`
:param load: `True` if we are at the init of the option description
:type load: bool
"""
if not load and self._consistencies is None:
self._state_consistencies = None
elif load and self._state_consistencies is None:
self._consistencies = None
del(self._state_consistencies)
else:
if load:
consistencies = self._state_consistencies
else:
consistencies = self._consistencies
if isinstance(consistencies, list):
new_value = []
for consistency in consistencies:
values = []
for obj in consistency[1]:
if load:
values.append(descr.impl_get_opt_by_path(obj))
else:
values.append(descr.impl_get_path_by_opt(obj))
new_value.append((consistency[0], tuple(values)))
else:
new_value = {}
for key, _consistencies in consistencies.items():
new_value[key] = []
for key_cons, _cons in _consistencies:
_list_cons = []
for _con in _cons:
if load:
_list_cons.append(
descr.impl_get_opt_by_path(_con))
else:
_list_cons.append(
descr.impl_get_path_by_opt(_con))
new_value[key].append((key_cons, tuple(_list_cons)))
if load:
del(self._state_consistencies)
self._consistencies = new_value
else:
self._state_consistencies = new_value
def _second_level_validation(self, value):
pass
class ChoiceOption(Option): class ChoiceOption(Option):
@ -566,7 +635,7 @@ class ChoiceOption(Option):
def __init__(self, name, doc, values, default=None, default_multi=None, def __init__(self, name, doc, values, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, open_values=False, validator=None, callback_params=None, open_values=False, validator=None,
validator_args=None, properties=()): validator_params=None, properties=None, warnings_only=False):
""" """
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
@ -584,8 +653,9 @@ class ChoiceOption(Option):
requires=requires, requires=requires,
multi=multi, multi=multi,
validator=validator, validator=validator,
validator_args=validator_args, validator_params=validator_params,
properties=properties) properties=properties,
warnings_only=warnings_only)
def impl_get_values(self): def impl_get_values(self):
return self._values return self._values
@ -662,7 +732,7 @@ class SymLinkOption(BaseOption):
__slots__ = ('_name', '_opt', '_state_opt') __slots__ = ('_name', '_opt', '_state_opt')
_opt_type = 'symlink' _opt_type = 'symlink'
#not return _opt consistencies #not return _opt consistencies
_consistencies = {} _consistencies = None
def __init__(self, name, opt): def __init__(self, name, opt):
self._name = name self._name = name
@ -688,23 +758,19 @@ class SymLinkOption(BaseOption):
del(self._state_opt) del(self._state_opt)
super(SymLinkOption, self)._impl_setstate(descr) super(SymLinkOption, self)._impl_setstate(descr)
def _impl_convert_consistencies(self, descr, load=False):
if load:
del(self._state_consistencies)
else:
self._state_consistencies = None
class IPOption(Option): class IPOption(Option):
"represents the choice of an ip" "represents the choice of an ip"
__slots__ = ('_only_private',) __slots__ = ('_private_only', '_allow_reserved')
_opt_type = 'ip' _opt_type = 'ip'
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None, callback_params=None, validator=None, validator_params=None,
properties=None, only_private=False): properties=None, private_only=False, allow_reserved=False,
self._only_private = only_private warnings_only=False):
self._private_only = private_only
self._allow_reserved = allow_reserved
super(IPOption, self).__init__(name, doc, default=default, super(IPOption, self).__init__(name, doc, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback, callback=callback,
@ -712,14 +778,21 @@ class IPOption(Option):
requires=requires, requires=requires,
multi=multi, multi=multi,
validator=validator, validator=validator,
validator_args=validator_args, validator_params=validator_params,
properties=properties) properties=properties,
warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value):
try:
IP('{0}/32'.format(value))
except ValueError:
raise ValueError(_('invalid IP {0}').format(self._name))
def _second_level_validation(self, value):
ip = IP('{0}/32'.format(value)) ip = IP('{0}/32'.format(value))
if ip.iptype() == 'RESERVED': if not self._allow_reserved and ip.iptype() == 'RESERVED':
raise ValueError(_("IP mustn't not be in reserved class")) raise ValueError(_("IP mustn't not be in reserved class"))
if self._only_private and not ip.iptype() == 'PRIVATE': if self._private_only and not ip.iptype() == 'PRIVATE':
raise ValueError(_("IP must be in private class")) raise ValueError(_("IP must be in private class"))
@ -738,10 +811,10 @@ class PortOption(Option):
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None, callback_params=None, validator=None, validator_params=None,
properties=None, allow_range=False, allow_zero=False, properties=None, allow_range=False, allow_zero=False,
allow_wellknown=True, allow_registred=True, allow_wellknown=True, allow_registred=True,
allow_private=False): allow_private=False, warnings_only=False):
self._allow_range = allow_range self._allow_range = allow_range
self._min_value = None self._min_value = None
self._max_value = None self._max_value = None
@ -772,8 +845,9 @@ class PortOption(Option):
requires=requires, requires=requires,
multi=multi, multi=multi,
validator=validator, validator=validator,
validator_args=validator_args, validator_params=validator_params,
properties=properties) properties=properties,
warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value):
if self._allow_range and ":" in str(value): if self._allow_range and ":" in str(value):
@ -798,9 +872,15 @@ class NetworkOption(Option):
_opt_type = 'network' _opt_type = 'network'
def _validate(self, value): def _validate(self, value):
try:
IP(value)
except ValueError:
raise ValueError(_('invalid network address {0}').format(self._name))
def _second_level_validation(self, value):
ip = IP(value) ip = IP(value)
if ip.iptype() == 'RESERVED': if ip.iptype() == 'RESERVED':
raise ValueError(_("network mustn't not be in reserved class")) raise ValueError(_("network shall not be in reserved class"))
class NetmaskOption(Option): class NetmaskOption(Option):
@ -809,18 +889,26 @@ class NetmaskOption(Option):
_opt_type = 'netmask' _opt_type = 'netmask'
def _validate(self, value): def _validate(self, value):
IP('0.0.0.0/{0}'.format(value)) try:
IP('0.0.0.0/{0}'.format(value))
except ValueError:
raise ValueError(_('invalid netmask address {0}').format(self._name))
def _cons_network_netmask(self, optname, value, value_): def _cons_network_netmask(self, opts, vals):
#opts must be (netmask, network) options #opts must be (netmask, network) options
self.__cons_netmask(optname, value, value_, False) if None in vals:
return
self.__cons_netmask(opts, vals[0], vals[1], False)
def _cons_ip_netmask(self, optname, value, value_): def _cons_ip_netmask(self, opts, vals):
#opts must be (netmask, ip) options #opts must be (netmask, ip) options
self.__cons_netmask(optname, value, value_, True) if None in vals:
return
self.__cons_netmask(opts, vals[0], vals[1], True)
#def __cons_netmask(self, opt, value, context, index, opts, make_net): def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net):
def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): if len(opts) != 2:
raise ConfigError(_('invalid len for opts'))
msg = None msg = None
try: try:
ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
@ -846,23 +934,48 @@ class NetmaskOption(Option):
else: else:
msg = _("invalid network {0} ({1}) with netmask {2} ({3})") msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
if msg is not None: if msg is not None:
raise ValueError(msg.format(val_ipnetwork, optname, raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
val_netmask, self._name)) val_netmask, self._name))
class BroadcastOption(Option):
__slots__ = tuple()
_opt_type = 'broadcast'
def _validate(self, value):
try:
IP('{0}/32'.format(value))
except ValueError:
raise ValueError(_('invalid broadcast address {0}').format(self._name))
def _cons_broadcast(self, opts, vals):
if len(vals) != 3:
raise ConfigError(_('invalid len for vals'))
if None in vals:
return
broadcast, network, netmask = vals
if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
'({3}) and netmask {4} ({5})').format(
broadcast, opts[0]._name, network,
opts[1]._name, netmask, opts[2]._name))
class DomainnameOption(Option): class DomainnameOption(Option):
"represents the choice of a domain name" """represents the choice of a domain name
netbios: for MS domain
hostname: to identify the device
domainname:
fqdn: with tld, not supported yet
"""
__slots__ = ('_type', '_allow_ip') __slots__ = ('_type', '_allow_ip')
_opt_type = 'domainname' _opt_type = 'domainname'
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None, callback_params=None, validator=None, validator_params=None,
properties=None, allow_ip=False, type_='domainname'): properties=None, allow_ip=False, type_='domainname',
#netbios: for MS domain warnings_only=False):
#hostname: to identify the device
#domainname:
#fqdn: with tld, not supported yet
if type_ not in ['netbios', 'hostname', 'domainname']: if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_)) raise ValueError(_('unknown type_ {0} for hostname').format(type_))
self._type = type_ self._type = type_
@ -876,8 +989,9 @@ class DomainnameOption(Option):
requires=requires, requires=requires,
multi=multi, multi=multi,
validator=validator, validator=validator,
validator_args=validator_args, validator_params=validator_params,
properties=properties) properties=properties,
warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value):
if self._allow_ip is True: if self._allow_ip is True:
@ -915,9 +1029,9 @@ class OptionDescription(BaseOption):
""" """
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type', __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
'_state_group_type', '_properties', '_children', '_state_group_type', '_properties', '_children',
'_consistencies', '_calc_properties', '__weakref__', '_cache_consistencies', '_calc_properties', '__weakref__',
'_readonly', '_impl_informations', '_state_requires', '_readonly', '_impl_informations', '_state_requires',
'_state_consistencies', '_stated', '_state_readonly') '_stated', '_state_readonly')
_opt_type = 'optiondescription' _opt_type = 'optiondescription'
def __init__(self, name, doc, children, requires=None, properties=None): def __init__(self, name, doc, children, requires=None, properties=None):
@ -938,6 +1052,7 @@ class OptionDescription(BaseOption):
old = child old = child
self._children = (tuple(child_names), tuple(children)) self._children = (tuple(child_names), tuple(children))
self._cache_paths = None self._cache_paths = None
self._cache_consistencies = None
# the group_type is useful for filtering OptionDescriptions in a config # the group_type is useful for filtering OptionDescriptions in a config
self._group_type = groups.default self._group_type = groups.default
@ -1011,12 +1126,11 @@ class OptionDescription(BaseOption):
if not force_no_consistencies and \ if not force_no_consistencies and \
option._consistencies is not None: option._consistencies is not None:
for consistency in option._consistencies: for consistency in option._consistencies:
func, opt = consistency func, all_cons_opts = consistency
opts = (option, opt) for opt in all_cons_opts:
_consistencies.setdefault(opt, _consistencies.setdefault(opt,
[]).append((func, opts)) []).append((func,
_consistencies.setdefault(option, all_cons_opts))
[]).append((func, opts))
else: else:
_currpath.append(attr) _currpath.append(attr)
option.impl_build_cache(cache_path, option.impl_build_cache(cache_path,
@ -1028,7 +1142,12 @@ class OptionDescription(BaseOption):
if save: if save:
self._cache_paths = (tuple(cache_option), tuple(cache_path)) self._cache_paths = (tuple(cache_option), tuple(cache_path))
if not force_no_consistencies: if not force_no_consistencies:
self._consistencies = _consistencies if _consistencies != {}:
self._cache_consistencies = {}
for opt, cons in _consistencies.items():
if opt not in cache_option:
raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name))
self._cache_consistencies[opt] = tuple(cons)
self._readonly = True self._readonly = True
def impl_get_opt_by_path(self, path): def impl_get_opt_by_path(self, path):
@ -1099,17 +1218,18 @@ class OptionDescription(BaseOption):
def impl_get_group_type(self): def impl_get_group_type(self):
return self._group_type return self._group_type
def _valid_consistency(self, opt, value, context=None, index=None): def _valid_consistency(self, option, value, context, index):
consistencies = self._consistencies.get(opt) if self._cache_consistencies is None:
return True
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
consistencies = self._cache_consistencies.get(option)
if consistencies is not None: if consistencies is not None:
for consistency in consistencies: for func, all_cons_opts in consistencies:
opt_ = consistency[1] #all_cons_opts[0] is the option where func is set
ret = opt_[0]._launch_consistency(consistency[0], ret = all_cons_opts[0]._launch_consistency(func, option,
opt, value,
value, context, index,
context, all_cons_opts)
index,
opt_[1])
if ret is False: if ret is False:
return False return False
return True return True
@ -1149,6 +1269,7 @@ class OptionDescription(BaseOption):
""" """
if descr is None: if descr is None:
self._cache_paths = None self._cache_paths = None
self._cache_consistencies = None
self.impl_build_cache(force_no_consistencies=True) self.impl_build_cache(force_no_consistencies=True)
descr = self descr = self
self._group_type = getattr(groups, self._state_group_type) self._group_type = getattr(groups, self._state_group_type)
@ -1252,3 +1373,34 @@ def validate_requires_arg(requires, name):
require[3], require[4], require[5])) require[3], require[4], require[5]))
ret.append(tuple(ret_action)) ret.append(tuple(ret_action))
return frozenset(config_action.keys()), tuple(ret) return frozenset(config_action.keys()), tuple(ret)
def validate_callback(callback, callback_params, type_):
if type(callback) != FunctionType:
raise ValueError(_('{0} should be a function').format(type_))
if callback_params is not None:
if not isinstance(callback_params, dict):
raise ValueError(_('{0}_params should be a dict').format(type_))
for key, callbacks in callback_params.items():
if key != '' and len(callbacks) != 1:
raise ValueError(_('{0}_params with key {1} should not have '
'length different to 1').format(type_,
key))
if not isinstance(callbacks, tuple):
raise ValueError(_('{0}_params should be tuple for key "{1}"'
).format(type_, key))
for callbk in callbacks:
if isinstance(callbk, tuple):
option, force_permissive = callbk
if type_ == 'validator' and not force_permissive:
raise ValueError(_('validator not support tuple'))
if not isinstance(option, Option) and not \
isinstance(option, SymLinkOption):
raise ValueError(_('{0}_params should have an option '
'not a {0} for first argument'
).format(type_, type(option)))
if force_permissive not in [True, False]:
raise ValueError(_('{0}_params should have a boolean'
' not a {0} for second argument'
).format(type_, type(
force_permissive)))

View File

@ -105,7 +105,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory'])
# ____________________________________________________________ # ____________________________________________________________
class _NameSpace: class _NameSpace(object):
"""convenient class that emulates a module """convenient class that emulates a module
and builds constants (that is, unique names) and builds constants (that is, unique names)
when attribute is added, we cannot delete it when attribute is added, we cannot delete it
@ -385,13 +385,17 @@ class Settings(object):
#____________________________________________________________ #____________________________________________________________
def validate_properties(self, opt_or_descr, is_descr, is_write, path, def validate_properties(self, opt_or_descr, is_descr, is_write, path,
value=None, force_permissive=False, value=None, force_permissive=False,
force_properties=None): force_properties=None, force_permissives=None):
""" """
validation upon the properties related to `opt_or_descr` validation upon the properties related to `opt_or_descr`
:param opt_or_descr: an option or an option description object :param opt_or_descr: an option or an option description object
:param force_permissive: behaves as if the permissive property :param force_permissive: behaves as if the permissive property
was present was present
:param force_properties: set() with properties that is force to add
in global properties
:param force_permissives: set() with permissives that is force to add
in global permissives
:param is_descr: we have to know if we are in an option description, :param is_descr: we have to know if we are in an option description,
just because the mandatory property just because the mandatory property
doesn't exist here doesn't exist here
@ -408,6 +412,8 @@ class Settings(object):
self_properties = copy(self._getproperties()) self_properties = copy(self._getproperties())
if force_permissive is True or 'permissive' in self_properties: if force_permissive is True or 'permissive' in self_properties:
properties -= self._p_.getpermissive() properties -= self._p_.getpermissive()
if force_permissives is not None:
properties -= force_permissives
# global properties # global properties
if force_properties is not None: if force_properties is not None:
@ -585,3 +591,23 @@ class Settings(object):
:returns: path :returns: path
""" """
return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt)
def get_modified_properties(self):
return self._p_.get_modified_properties()
def get_modified_permissives(self):
return self._p_.get_modified_permissives()
def __getstate__(self):
return {'_p_': self._p_, '_owner': str(self._owner)}
def _impl_setstate(self, storage):
self._p_._storage = storage
def __setstate__(self, states):
self._p_ = states['_p_']
try:
self._owner = getattr(owners, states['_owner'])
except AttributeError:
owners.addowner(states['_owner'])
self._owner = getattr(owners, states['_owner'])

View File

@ -68,7 +68,7 @@ class StorageType(object):
storage_type = StorageType() storage_type = StorageType()
def set_storage(name, **args): def set_storage(name, **kwargs):
"""Change storage's configuration """Change storage's configuration
:params name: is the storage name. If storage is already set, cannot :params name: is the storage name. If storage is already set, cannot
@ -77,16 +77,31 @@ def set_storage(name, **args):
Other attributes are differents according to the selected storage's name Other attributes are differents according to the selected storage's name
""" """
storage_type.set(name) storage_type.set(name)
settings = storage_type.get().Setting() setting = storage_type.get().setting
for option, value in args.items(): for option, value in kwargs.items():
try: try:
getattr(settings, option) getattr(setting, option)
setattr(settings, option, value) setattr(setting, option, value)
except AttributeError: except AttributeError:
raise ValueError(_('option {0} not already exists in storage {1}' raise ValueError(_('option {0} not already exists in storage {1}'
'').format(option, name)) '').format(option, name))
def _impl_getstate_setting():
setting = storage_type.get().setting
state = {'name': storage_type.storage_type}
for var in dir(setting):
if not var.startswith('_'):
state[var] = getattr(setting, var)
return state
def get_storage(session_id, persistent, test):
"""all used when __setstate__ a Config
"""
return storage_type.get().Storage(session_id, persistent, test)
def get_storages(context, session_id, persistent): def get_storages(context, session_id, persistent):
def gen_id(config): def gen_id(config):
return str(id(config)) + str(time()) return str(id(config)) + str(time())

View File

@ -26,6 +26,6 @@ use it. But if something goes wrong, you will lost your modifications.
""" """
from .value import Values from .value import Values
from .setting import Settings from .setting import Settings
from .storage import Setting, Storage, list_sessions, delete_session from .storage import setting, Storage, list_sessions, delete_session
__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session) __all__ = (setting, Values, Settings, Storage, list_sessions, delete_session)

View File

@ -17,7 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from ..cache import Cache from ..util import Cache
class Settings(Cache): class Settings(Cache):
@ -50,12 +50,21 @@ class Settings(Cache):
except KeyError: except KeyError:
pass pass
def get_properties(self, context):
return self._properties
# permissive # permissive
def setpermissive(self, path, permissive): def setpermissive(self, path, permissive):
self._permissives[path] = frozenset(permissive) self._permissives[path] = frozenset(permissive)
def getpermissive(self, path=None): def getpermissive(self, path=None):
return self._permissives.get(path, frozenset()) return self._permissives.get(path, frozenset())
def get_modified_properties(self):
"""return all modified settings in a dictionary
example: {'path1': set(['prop1', 'prop2'])}
"""
return self._properties
def get_modified_permissives(self):
"""return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])}
"""
return self._permissives

View File

@ -18,9 +18,10 @@
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.error import ConfigError from tiramisu.error import ConfigError
from ..util import SerializeObject
class Setting(object): class Setting(SerializeObject):
"""Dictionary storage has no particular setting. """Dictionary storage has no particular setting.
""" """
pass pass
@ -39,15 +40,18 @@ def delete_session(session_id):
class Storage(object): class Storage(object):
__slots__ = ('session_id', 'values', 'settings') __slots__ = ('session_id', 'persistent')
storage = 'dictionary' storage = 'dictionary'
#if object could be serializable
serializable = True
def __init__(self, session_id, persistent): def __init__(self, session_id, persistent, test=False):
if session_id in _list_sessions: if not test and session_id in _list_sessions:
raise ValueError(_('session already used')) raise ValueError(_('session already used'))
if persistent: if persistent:
raise ValueError(_('a dictionary cannot be persistent')) raise ValueError(_('a dictionary cannot be persistent'))
self.session_id = session_id self.session_id = session_id
self.persistent = persistent
_list_sessions.append(self.session_id) _list_sessions.append(self.session_id)
def __del__(self): def __del__(self):

View File

@ -18,7 +18,7 @@
# #
# ____________________________________________________________ # ____________________________________________________________
from ..cache import Cache from ..util import Cache
class Values(Cache): class Values(Cache):

View File

@ -24,6 +24,6 @@ You should not configure differents Configs with same session_id.
""" """
from .value import Values from .value import Values
from .setting import Settings from .setting import Settings
from .storage import Setting, Storage, list_sessions, delete_session from .storage import setting, Storage, list_sessions, delete_session
__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session) __all__ = (setting, Values, Settings, Storage, list_sessions, delete_session)

View File

@ -30,22 +30,22 @@ class Settings(Sqlite3DB):
permissives_table += 'primary key, permissives text)' permissives_table += 'primary key, permissives text)'
# should init cache too # should init cache too
super(Settings, self).__init__(storage) super(Settings, self).__init__(storage)
self.storage.execute(settings_table, commit=False) self._storage.execute(settings_table, commit=False)
self.storage.execute(permissives_table) self._storage.execute(permissives_table)
# propertives # propertives
def setproperties(self, path, properties): def setproperties(self, path, properties):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM property WHERE path = ?", (path,), self._storage.execute("DELETE FROM property WHERE path = ?", (path,),
False) False)
self.storage.execute("INSERT INTO property(path, properties) VALUES " self._storage.execute("INSERT INTO property(path, properties) VALUES "
"(?, ?)", (path, "(?, ?)", (path,
self._sqlite_encode(properties))) self._sqlite_encode(properties)))
def getproperties(self, path, default_properties): def getproperties(self, path, default_properties):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
value = self.storage.select("SELECT properties FROM property WHERE " value = self._storage.select("SELECT properties FROM property WHERE "
"path = ?", (path,)) "path = ?", (path,))
if value is None: if value is None:
return set(default_properties) return set(default_properties)
else: else:
@ -53,42 +53,53 @@ class Settings(Sqlite3DB):
def hasproperties(self, path): def hasproperties(self, path):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
return self.storage.select("SELECT properties FROM property WHERE " return self._storage.select("SELECT properties FROM property WHERE "
"path = ?", (path,)) is not None "path = ?", (path,)) is not None
def reset_all_propertives(self): def reset_all_propertives(self):
self.storage.execute("DELETE FROM property") self._storage.execute("DELETE FROM property")
def reset_properties(self, path): def reset_properties(self, path):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM property WHERE path = ?", (path,)) self._storage.execute("DELETE FROM property WHERE path = ?", (path,))
def get_properties(self, context):
"""return all properties in a dictionary
"""
ret = {}
for path, properties in self.storage.select("SELECT * FROM property",
only_one=False):
path = self._sqlite_decode_path(path)
properties = self._sqlite_decode(properties)
ret[path] = properties
return ret
# permissive # permissive
def setpermissive(self, path, permissive): def setpermissive(self, path, permissive):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM permissive WHERE path = ?", (path,), self._storage.execute("DELETE FROM permissive WHERE path = ?", (path,),
False) False)
self.storage.execute("INSERT INTO permissive(path, permissives) " self._storage.execute("INSERT INTO permissive(path, permissives) "
"VALUES (?, ?)", (path, "VALUES (?, ?)", (path,
self._sqlite_encode(permissive) self._sqlite_encode(permissive)
)) ))
def getpermissive(self, path='_none'): def getpermissive(self, path='_none'):
permissives = self.storage.select("SELECT permissives FROM " permissives = self._storage.select("SELECT permissives FROM "
"permissive WHERE path = ?", "permissive WHERE path = ?",
(path,)) (path,))
if permissives is None: if permissives is None:
return frozenset() return frozenset()
else: else:
return frozenset(self._sqlite_decode(permissives[0])) return frozenset(self._sqlite_decode(permissives[0]))
def get_modified_properties(self):
"""return all modified settings in a dictionary
example: {'path1': set(['prop1', 'prop2'])}
"""
ret = {}
for path, properties in self._storage.select("SELECT * FROM property",
only_one=False):
path = self._sqlite_decode_path(path)
ret[path] = self._sqlite_decode(properties)
return ret
def get_modified_permissives(self):
"""return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])}
"""
ret = {}
for path, permissives in self._storage.select("SELECT * FROM permissive",
only_one=False):
path = self._sqlite_decode_path(path)
ret[path] = self._sqlite_decode(permissives)
return ret

View File

@ -17,8 +17,11 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from cPickle import loads, dumps try:
from ..cache import Cache from cPickle import loads, dumps
except ImportError:
from pickle import loads, dumps
from ..util import Cache
class Sqlite3DB(Cache): class Sqlite3DB(Cache):

View File

@ -22,9 +22,10 @@ from os import unlink
from os.path import basename, splitext, join from os.path import basename, splitext, join
import sqlite3 import sqlite3
from glob import glob from glob import glob
from ..util import SerializeObject
class Setting(object): class Setting(SerializeObject):
""":param extension: database file extension (by default: db) """:param extension: database file extension (by default: db)
:param dir_database: root database directory (by default: /tmp) :param dir_database: root database directory (by default: /tmp)
""" """
@ -52,13 +53,17 @@ def delete_session(session_id):
class Storage(object): class Storage(object):
__slots__ = ('_conn', '_cursor', 'persistent', '_session_id') __slots__ = ('_conn', '_cursor', 'persistent', 'session_id', 'serializable')
storage = 'sqlite3' storage = 'sqlite3'
def __init__(self, session_id, persistent): def __init__(self, session_id, persistent, test=False):
self.persistent = persistent self.persistent = persistent
self._session_id = session_id if self.persistent:
self._conn = sqlite3.connect(_gen_filename(self._session_id)) self.serializable = True
else:
self.serializable = False
self.session_id = session_id
self._conn = sqlite3.connect(_gen_filename(self.session_id))
self._conn.text_factory = str self._conn.text_factory = str
self._cursor = self._conn.cursor() self._cursor = self._conn.cursor()
@ -80,4 +85,4 @@ class Storage(object):
self._cursor.close() self._cursor.close()
self._conn.close() self._conn.close()
if not self.persistent: if not self.persistent:
delete_session(self._session_id) delete_session(self.session_id)

View File

@ -32,11 +32,11 @@ class Values(Sqlite3DB):
super(Values, self).__init__(storage) super(Values, self).__init__(storage)
values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary ' values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary '
values_table += 'key, value text, owner text)' values_table += 'key, value text, owner text)'
self.storage.execute(values_table, commit=False) self._storage.execute(values_table, commit=False)
informations_table = 'CREATE TABLE IF NOT EXISTS information(key text primary ' informations_table = 'CREATE TABLE IF NOT EXISTS information(key text primary '
informations_table += 'key, value text)' informations_table += 'key, value text)'
self.storage.execute(informations_table) self._storage.execute(informations_table)
for owner in self.storage.select("SELECT DISTINCT owner FROM value", tuple(), False): for owner in self._storage.select("SELECT DISTINCT owner FROM value", tuple(), False):
try: try:
getattr(owners, owner[0]) getattr(owners, owner[0])
except AttributeError: except AttributeError:
@ -44,7 +44,7 @@ class Values(Sqlite3DB):
# sqlite # sqlite
def _sqlite_select(self, path): def _sqlite_select(self, path):
return self.storage.select("SELECT value FROM value WHERE path = ?", return self._storage.select("SELECT value FROM value WHERE path = ?",
(path,)) (path,))
# value # value
@ -54,7 +54,7 @@ class Values(Sqlite3DB):
""" """
self.resetvalue(path) self.resetvalue(path)
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("INSERT INTO value(path, value, owner) VALUES " self._storage.execute("INSERT INTO value(path, value, owner) VALUES "
"(?, ?, ?)", (path, self._sqlite_encode(value), "(?, ?, ?)", (path, self._sqlite_encode(value),
str(owner))) str(owner)))
@ -76,14 +76,14 @@ class Values(Sqlite3DB):
"""remove value means delete value in storage """remove value means delete value in storage
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM value WHERE path = ?", (path,)) self._storage.execute("DELETE FROM value WHERE path = ?", (path,))
def get_modified_values(self): def get_modified_values(self):
"""return all values in a dictionary """return all values in a dictionary
example: {option1: (owner, 'value1'), option2: (owner, 'value2')} example: {option1: (owner, 'value1'), option2: (owner, 'value2')}
""" """
ret = {} ret = {}
for path, value, owner in self.storage.select("SELECT * FROM value", for path, value, owner in self._storage.select("SELECT * FROM value",
only_one=False): only_one=False):
path = self._sqlite_decode_path(path) path = self._sqlite_decode_path(path)
owner = getattr(owners, owner) owner = getattr(owners, owner)
@ -97,7 +97,7 @@ class Values(Sqlite3DB):
"""change owner for an option """change owner for an option
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self.storage.execute("UPDATE value SET owner = ? WHERE path = ?", self._storage.execute("UPDATE value SET owner = ? WHERE path = ?",
(str(owner), path)) (str(owner), path))
def getowner(self, path, default): def getowner(self, path, default):
@ -105,7 +105,7 @@ class Values(Sqlite3DB):
return: owner object return: owner object
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
owner = self.storage.select("SELECT owner FROM value WHERE path = ?", owner = self._storage.select("SELECT owner FROM value WHERE path = ?",
(path,)) (path,))
if owner is None: if owner is None:
return default return default
@ -125,9 +125,9 @@ class Values(Sqlite3DB):
:param key: information's key (ex: "help", "doc" :param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string") :param value: information's value (ex: "the help string")
""" """
self.storage.execute("DELETE FROM information WHERE key = ?", (key,), self._storage.execute("DELETE FROM information WHERE key = ?", (key,),
False) False)
self.storage.execute("INSERT INTO information(key, value) VALUES " self._storage.execute("INSERT INTO information(key, value) VALUES "
"(?, ?)", (key, self._sqlite_encode(value))) "(?, ?)", (key, self._sqlite_encode(value)))
def get_information(self, key): def get_information(self, key):
@ -135,7 +135,7 @@ class Values(Sqlite3DB):
:param key: the item string (ex: "help") :param key: the item string (ex: "help")
""" """
value = self.storage.select("SELECT value FROM information WHERE key = ?", value = self._storage.select("SELECT value FROM information WHERE key = ?",
(key,)) (key,))
if value is None: if value is None:
raise ValueError("not found") raise ValueError("not found")

View File

@ -17,15 +17,65 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.setting import owners
class SerializeObject(object):
def __getstate__(self):
ret = {}
for key in dir(self):
if not key.startswith('__'):
ret[key] = getattr(self, key)
return ret
class Cache(object): class Cache(object):
__slots__ = ('_cache', 'storage') __slots__ = ('_cache', '_storage')
key_is_path = False key_is_path = False
def __init__(self, storage): def __init__(self, storage):
self._cache = {} self._cache = {}
self.storage = storage self._storage = storage
def __getstate__(self):
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
slots -= frozenset(['__weakref__', '_storage'])
states = {}
for slot in slots:
try:
value = getattr(self, slot)
#value has owners object, need 'str()' it
if slot == '_values':
_value = {}
for key, values in value.items():
vals = list(values)
vals[0] = str(vals[0])
_value[key] = tuple(vals)
states[slot] = _value
else:
states[slot] = value
except AttributeError:
pass
return states
def __setstate__(self, states):
for key, value in states.items():
#value has owners object, need to reconstruct it
if key == '_values':
_value = {}
for key_, values_ in value.items():
vals = list(values_)
try:
vals[0] = getattr(owners, vals[0])
except AttributeError:
owners.addowner(vals[0])
vals[0] = getattr(owners, vals[0])
_value[key_] = tuple(vals)
value = _value
setattr(self, key, value)
def setcache(self, path, val, time): def setcache(self, path, val, time):
self._cache[path] = (val, time) self._cache[path] = (val, time)

View File

@ -106,7 +106,8 @@ class Values(object):
path = self._get_opt_path(opt) path = self._get_opt_path(opt)
if self._p_.hasvalue(path): if self._p_.hasvalue(path):
setting = self.context().cfgimpl_get_settings() setting = self.context().cfgimpl_get_settings()
opt.impl_validate(opt.impl_getdefault(), self.context(), opt.impl_validate(opt.impl_getdefault(),
self.context(),
'validator' in setting) 'validator' in setting)
self.context().cfgimpl_reset_cache() self.context().cfgimpl_reset_cache()
if (opt.impl_is_multi() and if (opt.impl_is_multi() and
@ -124,7 +125,7 @@ class Values(object):
return True return True
return False return False
def _getcallback_value(self, opt, index=None): def _getcallback_value(self, opt, index=None, max_len=None):
""" """
retrieves a value for the options that have a callback retrieves a value for the options that have a callback
@ -139,7 +140,7 @@ class Values(object):
return carry_out_calculation(opt._name, config=self.context(), return carry_out_calculation(opt._name, config=self.context(),
callback=callback, callback=callback,
callback_params=callback_params, callback_params=callback_params,
index=index) index=index, max_len=max_len)
def __getitem__(self, opt): def __getitem__(self, opt):
"enables us to use the pythonic dictionary-like access to values" "enables us to use the pythonic dictionary-like access to values"
@ -177,11 +178,20 @@ class Values(object):
# options with callbacks # options with callbacks
setting = self.context().cfgimpl_get_settings() setting = self.context().cfgimpl_get_settings()
is_frozen = 'frozen' in setting[opt] is_frozen = 'frozen' in setting[opt]
# For calculating properties, we need value (ie for mandatory value).
# If value is calculating with a PropertiesOptionError's option
# _getcallback_value raise a ConfigError.
# We can not raise ConfigError if this option should raise
# PropertiesOptionError too. So we get config_error and raise
# ConfigError if properties did not raise.
config_error = None
force_permissives = None
# if value is callback and is not set # if value is callback and is not set
# or frozen with force_default_on_freeze # or frozen with force_default_on_freeze
if opt.impl_has_callback() and ( if opt.impl_has_callback() and (
self._is_default_owner(path) or self._is_default_owner(path) or
(is_frozen and 'force_default_on_freeze' in setting[opt])): (is_frozen and 'force_default_on_freeze' in setting[opt])):
lenmaster = None
no_value_slave = False no_value_slave = False
if (opt.impl_is_multi() and if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.slave): opt.impl_get_multitype() == multitypes.slave):
@ -193,15 +203,25 @@ class Values(object):
no_value_slave = True no_value_slave = True
if not no_value_slave: if not no_value_slave:
value = self._getcallback_value(opt) try:
if (opt.impl_is_multi() and value = self._getcallback_value(opt, max_len=lenmaster)
opt.impl_get_multitype() == multitypes.slave): except ConfigError as err:
if not isinstance(value, list): # cannot assign config_err directly in python 3.3
value = [value for i in range(lenmaster)] config_error = err
if opt.impl_is_multi(): value = None
value = Multi(value, self.context, opt, path, validate) # should not raise PropertiesOptionError if option is
# suppress value if already set # mandatory
self.reset(opt, path) force_permissives = set(['mandatory'])
else:
if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.slave):
if not isinstance(value, list):
value = [value for i in range(lenmaster)]
if config_error is None:
if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, validate)
# suppress value if already set
self.reset(opt, path)
# frozen and force default # frozen and force default
elif is_frozen and 'force_default_on_freeze' in setting[opt]: elif is_frozen and 'force_default_on_freeze' in setting[opt]:
value = self._getdefault(opt) value = self._getdefault(opt)
@ -209,15 +229,18 @@ class Values(object):
value = Multi(value, self.context, opt, path, validate) value = Multi(value, self.context, opt, path, validate)
else: else:
value = self._getvalue(opt, path, validate) value = self._getvalue(opt, path, validate)
if validate: if config_error is None and validate:
opt.impl_validate(value, self.context(), 'validator' in setting) opt.impl_validate(value, self.context(), 'validator' in setting)
if self._is_default_owner(path) and \ if config_error is None and self._is_default_owner(path) and \
'force_store_value' in setting[opt]: 'force_store_value' in setting[opt]:
self.setitem(opt, value, path, is_write=False) self.setitem(opt, value, path, is_write=False)
if validate_properties: if validate_properties:
setting.validate_properties(opt, False, False, value=value, path=path, setting.validate_properties(opt, False, False, value=value, path=path,
force_permissive=force_permissive, force_permissive=force_permissive,
force_properties=force_properties) force_properties=force_properties,
force_permissives=force_permissives)
if config_error is not None:
raise config_error
return value return value
def __setitem__(self, opt, value): def __setitem__(self, opt, value):
@ -231,7 +254,7 @@ class Values(object):
opt.impl_validate(value, self.context(), opt.impl_validate(value, self.context(),
'validator' in self.context().cfgimpl_get_settings()) 'validator' in self.context().cfgimpl_get_settings())
if opt.impl_is_multi() and not isinstance(value, Multi): if opt.impl_is_multi() and not isinstance(value, Multi):
value = Multi(value, self.context, opt, path) value = Multi(value, self.context, opt, path, setitem=True)
self._setvalue(opt, path, value, force_permissive=force_permissive, self._setvalue(opt, path, value, force_permissive=force_permissive,
is_write=is_write) is_write=is_write)
@ -339,6 +362,15 @@ class Values(object):
raise ValueError(_("information's item" raise ValueError(_("information's item"
" not found: {0}").format(key)) " not found: {0}").format(key))
def __getstate__(self):
return {'_p_': self._p_}
def _impl_setstate(self, storage):
self._p_._storage = storage
def __setstate__(self, states):
self._p_ = states['_p_']
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
@ -349,11 +381,13 @@ class Multi(list):
that support item notation for the values of multi options""" that support item notation for the values of multi options"""
__slots__ = ('opt', 'path', 'context') __slots__ = ('opt', 'path', 'context')
def __init__(self, value, context, opt, path, validate=True): def __init__(self, value, context, opt, path, validate=True,
setitem=False):
""" """
:param value: the Multi wraps a list value :param value: the Multi wraps a list value
:param context: the home config that has the values :param context: the home config that has the values
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
:param setitem: only if set a value
""" """
self.opt = opt self.opt = opt
self.path = path self.path = path
@ -363,27 +397,35 @@ class Multi(list):
if not isinstance(value, list): if not isinstance(value, list):
value = [value] value = [value]
if validate and self.opt.impl_get_multitype() == multitypes.slave: if validate and self.opt.impl_get_multitype() == multitypes.slave:
value = self._valid_slave(value) value = self._valid_slave(value, setitem)
elif self.opt.impl_get_multitype() == multitypes.master: elif validate and self.opt.impl_get_multitype() == multitypes.master:
self._valid_master(value) self._valid_master(value)
super(Multi, self).__init__(value) super(Multi, self).__init__(value)
def _valid_slave(self, value): def _valid_slave(self, value, setitem):
#if slave, had values until master's one #if slave, had values until master's one
values = self.context().cfgimpl_get_values()
masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt( masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt(
self.opt.impl_get_master_slaves()) self.opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp) mastervalue = getattr(self.context(), masterp)
masterlen = len(mastervalue) masterlen = len(mastervalue)
valuelen = len(value) valuelen = len(value)
is_default_owner = not values._is_default_owner(self.path) or setitem
if valuelen > masterlen or (valuelen < masterlen and if valuelen > masterlen or (valuelen < masterlen and
not self.context().cfgimpl_get_values( is_default_owner):
)._is_default_owner(self.path)):
raise SlaveError(_("invalid len for the slave: {0}" raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format( " which has {1} as master").format(
self.opt._name, masterp)) self.opt._name, masterp))
elif valuelen < masterlen: elif valuelen < masterlen:
for num in range(0, masterlen - valuelen): for num in range(0, masterlen - valuelen):
value.append(self.opt.impl_getdefault_multi()) if self.opt.impl_has_callback():
# if callback add a value, but this value will not change
# anymore automaticly (because this value has owner)
index = value.__len__()
value.append(values._getcallback_value(self.opt,
index=index))
else:
value.append(self.opt.impl_getdefault_multi())
#else: same len so do nothing #else: same len so do nothing
return value return value
@ -401,13 +443,22 @@ class Multi(list):
self.opt._name, slave._name)) self.opt._name, slave._name))
elif len(value_slave) < masterlen: elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)): for num in range(0, masterlen - len(value_slave)):
value_slave.append(slave.impl_getdefault_multi(), if slave.impl_has_callback():
force=True) # if callback add a value, but this value will not
# change anymore automaticly (because this value
# has owner)
index = value_slave.__len__()
value_slave.append(
values._getcallback_value(slave, index=index),
force=True)
else:
value_slave.append(slave.impl_getdefault_multi(),
force=True)
def __setitem__(self, key, value): def __setitem__(self, index, value):
self._validate(value) self._validate(value, index)
#assume not checking mandatory property #assume not checking mandatory property
super(Multi, self).__setitem__(key, value) super(Multi, self).__setitem__(index, value)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def append(self, value, force=False): def append(self, value, force=False):
@ -425,15 +476,17 @@ class Multi(list):
#Force None il return a list #Force None il return a list
if isinstance(value, list): if isinstance(value, list):
value = None value = None
self._validate(value) index = self.__len__()
self._validate(value, index)
super(Multi, self).append(value) super(Multi, self).append(value)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path,
self,
validate_properties=not force)
if not force and self.opt.impl_get_multitype() == multitypes.master: if not force and self.opt.impl_get_multitype() == multitypes.master:
for slave in self.opt.impl_get_master_slaves(): for slave in self.opt.impl_get_master_slaves():
path = values._get_opt_path(slave) path = values._get_opt_path(slave)
if not values._is_default_owner(path): if not values._is_default_owner(path):
if slave.impl_has_callback(): if slave.impl_has_callback():
index = self.__len__() - 1
dvalue = values._getcallback_value(slave, index=index) dvalue = values._getcallback_value(slave, index=index)
else: else:
dvalue = slave.impl_getdefault_multi() dvalue = slave.impl_getdefault_multi()
@ -485,22 +538,26 @@ class Multi(list):
super(Multi, self).extend(iterable) super(Multi, self).extend(iterable)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def _validate(self, value): def _validate(self, value, force_index):
if value is not None: if value is not None:
try: try:
self.opt._validate(value) self.opt.impl_validate(value, context=self.context(),
force_index=force_index)
except ValueError as err: except ValueError as err:
raise ValueError(_("invalid value {0} " raise ValueError(_("invalid value {0} "
"for option {1}: {2}" "for option {1}: {2}"
"").format(str(value), "").format(str(value),
self.opt._name, err)) self.opt._name, err))
def pop(self, key, force=False): def pop(self, index, force=False):
"""the list value can be updated (poped) """the list value can be updated (poped)
only if the option is a master only if the option is a master
:param key: index of the element to pop :param index: remove item a index
:return: the requested element :type index: int
:param force: force pop item (withoud check master/slave)
:type force: boolean
:returns: item at index
""" """
if not force: if not force:
if self.opt.impl_get_multitype() == multitypes.slave: if self.opt.impl_get_multitype() == multitypes.slave:
@ -513,8 +570,8 @@ class Multi(list):
#get multi without valid properties #get multi without valid properties
values.getitem(slave, values.getitem(slave,
validate_properties=False validate_properties=False
).pop(key, force=True) ).pop(index, force=True)
#set value without valid properties #set value without valid properties
ret = super(Multi, self).pop(key) ret = super(Multi, self).pop(index)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
return ret return ret

View File

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-08-31 09:52+CEST\n" "POT-Creation-Date: 2013-09-28 19:06+CEST\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n" "Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -11,438 +11,526 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n" "X-Generator: Poedit 1.5.4\n"
#: tiramisu/autolib.py:58 #: tiramisu/autolib.py:144
msgid "no config specified but needed"
msgstr "aucune config spécifié alors que c'est nécessaire"
#: tiramisu/autolib.py:65
msgid "" msgid ""
"unable to carry out a calculation, option {0} has properties: {1} for: {2}" "unable to carry out a calculation, option {0} has properties: {1} for: {2}"
msgstr "" msgstr ""
"impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : " "impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : "
"{2}" "{2}"
#: tiramisu/autolib.py:74 #: tiramisu/autolib.py:153
msgid "" msgid ""
"unable to carry out a calculation, option value with multi types must have " "unable to carry out a calculation, option value with multi types must have "
"same length for: {0}" "same length for: {0}"
msgstr "" msgstr ""
"impossible d'effectuer le calcul, valeur d'un option avec le type multi doit " "impossible d'effectuer le calcul, la valeur d'une option avec le type multi "
"avoir la même longueur pour : {0}" "doit avoir la même longueur pour : {0}"
#: tiramisu/config.py:47 #: tiramisu/config.py:51
msgid "descr must be an optiondescription, not {0}" msgid "descr must be an optiondescription, not {0}"
msgstr "descr doit être une optiondescription pas un {0}" msgstr "descr doit être une optiondescription pas un {0}"
#: tiramisu/config.py:121 #: tiramisu/config.py:126
msgid "unknown group_type: {0}" msgid "unknown group_type: {0}"
msgstr "group_type inconnu: {0}" msgstr "group_type inconnu: {0}"
#: tiramisu/config.py:157 #: tiramisu/config.py:162
msgid "" msgid ""
"no option description found for this config (may be metaconfig without meta)" "no option description found for this config (may be metaconfig without meta)"
msgstr "" msgstr ""
"pas d'option description pour cette config (peut être une metaconfig sans " "pas d'option description trouvé pour cette config (peut être une metaconfig "
"meta)" "sans meta)"
#: tiramisu/config.py:311 #: tiramisu/config.py:188
msgid "can't assign to an OptionDescription"
msgstr "ne peut pas attribuer une valeur à une OptionDescription"
#: tiramisu/config.py:319
msgid "unknown type_ type {0}for _find" msgid "unknown type_ type {0}for _find"
msgstr "type_ type {0} pour _find inconnu" msgstr "type_ type {0} pour _find inconnu"
#: tiramisu/config.py:350 #: tiramisu/config.py:358
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "aucune option trouvée dans la config avec ces critères" msgstr "aucune option trouvée dans la config avec ces critères"
#: tiramisu/config.py:400 #: tiramisu/config.py:408
msgid "make_dict can't filtering with value without option" msgid "make_dict can't filtering with value without option"
msgstr "make_dict ne peut filtrer sur une valeur mais sans option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option"
#: tiramisu/config.py:421 #: tiramisu/config.py:429
msgid "unexpected path {0}, should start with {1}" msgid "unexpected path {0}, should start with {1}"
msgstr "chemin imprévu {0}, devrait commencer par {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}"
#: tiramisu/config.py:481 #: tiramisu/config.py:489
msgid "opt in getowner must be an option not {0}" msgid "opt in getowner must be an option not {0}"
msgstr "opt dans getowner doit être une option pas {0}" msgstr "opt dans getowner doit être une option pas {0}"
#: tiramisu/option.py:71 #: tiramisu/option.py:68
msgid "{0} has no attribute impl_set_information"
msgstr "{0} n'a pas d'attribut impl_set_information"
#: tiramisu/option.py:86
msgid "information's item not found: {0}"
msgstr "aucune config spécifié alors que c'est nécessaire"
#: tiramisu/option.py:89
msgid "{0} has no attribute impl_get_information"
msgstr "{0} n'a pas d'attribut impl_get_information"
#: tiramisu/option.py:117
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul"
#: tiramisu/option.py:159
msgid "invalid name: {0} for option" msgid "invalid name: {0} for option"
msgstr "nom invalide : {0} pour l'option" msgstr "nom invalide : {0} pour l'option"
#: tiramisu/option.py:169 #: tiramisu/option.py:77
msgid "validator must be a function" msgid "invalid properties type {0} for {1}, must be a tuple"
msgstr "validator doit être une fonction" msgstr "type des properties invalide {0} pour {1}, doit être un tuple"
#: tiramisu/option.py:176 #: tiramisu/option.py:115
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule"
#: tiramisu/option.py:142 tiramisu/value.py:360
msgid "information's item not found: {0}"
msgstr "aucune config spécifié alors que c'est nécessaire"
#: tiramisu/option.py:204
msgid "cannot serialize Option, only in OptionDescription"
msgstr "ne peut serialiser une Option, seulement via une OptionDescription"
#: tiramisu/option.py:307
msgid "a default_multi is set whereas multi is False in option: {0}" msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr "" msgstr ""
"une default_multi est renseigné alors que multi est False dans l'option : {0}" "une default_multi est renseignée alors que multi est False dans l'option : "
"{0}"
#: tiramisu/option.py:182 #: tiramisu/option.py:313
msgid "invalid default_multi value {0} for option {1}: {2}" msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}"
#: tiramisu/option.py:187 #: tiramisu/option.py:318
msgid "default value not allowed if option: {0} is calculated" msgid "default value not allowed if option: {0} is calculated"
msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculé" msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée"
#: tiramisu/option.py:190 #: tiramisu/option.py:321
msgid "" msgid ""
"params defined for a callback function but no callback defined yet for " "params defined for a callback function but no callback defined yet for "
"option {0}" "option {0}"
msgstr "" msgstr ""
"params définit pour une fonction callback mais par de callback défini encore " "params définis pour une fonction callback mais par de callback encore "
"pour l'option {0}" "définis pour l'option {0}"
#: tiramisu/option.py:212 tiramisu/option.py:753 #: tiramisu/option.py:360
msgid "invalid properties type {0} for {1}, must be a tuple" msgid "option not in all_cons_opts"
msgstr "type des properties invalide {0} pour {1}, doit être un tuple" msgstr "option non présentante dans all_cons_opts"
#: tiramisu/option.py:285 #: tiramisu/option.py:432 tiramisu/value.py:545
msgid "invalid value {0} for option {1} for object {2}"
msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}"
#: tiramisu/option.py:293 tiramisu/value.py:468
msgid "invalid value {0} for option {1}: {2}" msgid "invalid value {0} for option {1}: {2}"
msgstr "valeur invalide {0} pour l'option {1} : {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}"
#: tiramisu/option.py:305 #: tiramisu/option.py:449
msgid "invalid value {0} for option {1} which must be a list" msgid "which must be a list"
msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" msgstr "lequel doit être une liste"
#: tiramisu/option.py:374 #: tiramisu/option.py:509
msgid "invalid value {0} for option {1} must be different as {2} option" msgid "consistency should be set with an option"
msgstr "consistency doit être configuré avec une option"
#: tiramisu/option.py:511
msgid "cannot add consistency with itself"
msgstr "ne peut ajouter une consistency avec lui même"
#: tiramisu/option.py:513
msgid "every options in consistency should be multi or none"
msgstr "" msgstr ""
"valeur invalide {0} pour l'option {1} doit être différent que l'option {2}" "toutes les options d'une consistency devrait être multi ou ne pas l'être"
#: tiramisu/option.py:396 #: tiramisu/option.py:533
msgid "same value for {0} and {1}"
msgstr "même valeur pour {0} et {1}"
#: tiramisu/option.py:642
msgid "values must be a tuple for {0}" msgid "values must be a tuple for {0}"
msgstr "values doit être un tuple pour {0}" msgstr "values doit être un tuple pour {0}"
#: tiramisu/option.py:399 #: tiramisu/option.py:645
msgid "open_values must be a boolean for {0}" msgid "open_values must be a boolean for {0}"
msgstr "open_values doit être un booléen pour {0}" msgstr "open_values doit être un booléen pour {0}"
#: tiramisu/option.py:420 #: tiramisu/option.py:667
msgid "value {0} is not permitted, only {1} is allowed" msgid "value {0} is not permitted, only {1} is allowed"
msgstr "valeur {0} n'est pas permit, seules {1} sont autorisées" msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées"
#: tiramisu/option.py:432 #: tiramisu/option.py:679
msgid "value must be a boolean" msgid "value must be a boolean"
msgstr "valeur doit être un booléen" msgstr "valeur doit être un booléen"
#: tiramisu/option.py:442 #: tiramisu/option.py:689
msgid "value must be an integer" msgid "value must be an integer"
msgstr "valeur doit être un numbre" msgstr "valeur doit être un nombre entier"
#: tiramisu/option.py:452 #: tiramisu/option.py:699
msgid "value must be a float" msgid "value must be a float"
msgstr "valeur doit être un nombre flottant" msgstr "valeur doit être un nombre flottant"
#: tiramisu/option.py:462 #: tiramisu/option.py:709
msgid "value must be a string, not {0}" msgid "value must be a string, not {0}"
msgstr "valeur doit être une chaîne, pas {0}" msgstr "valeur doit être une chaîne, pas {0}"
#: tiramisu/option.py:480 #: tiramisu/option.py:727
msgid "value must be an unicode" msgid "value must be an unicode"
msgstr "valeur doit être une valeur unicode" msgstr "valeur doit être une valeur unicode"
#: tiramisu/option.py:490 #: tiramisu/option.py:739
msgid "malformed symlinkoption must be an option for symlink {0}" msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr "symlinkoption mal formé doit être une option pour symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}"
#: tiramisu/option.py:526 #: tiramisu/option.py:788
msgid "invalid IP {0}"
msgstr "adresse IP invalide {0}"
#: tiramisu/option.py:793
msgid "IP mustn't not be in reserved class" msgid "IP mustn't not be in reserved class"
msgstr "IP ne doit pas être d'une classe reservée" msgstr "IP ne doit pas être d'une classe reservée"
#: tiramisu/option.py:528 #: tiramisu/option.py:795
msgid "IP must be in private class" msgid "IP must be in private class"
msgstr "IP doit être dans la classe privée" msgstr "IP doit être dans la classe privée"
#: tiramisu/option.py:566 #: tiramisu/option.py:833
msgid "inconsistency in allowed range" msgid "inconsistency in allowed range"
msgstr "inconsistence dans la plage autorisée" msgstr "inconsistence dans la plage autorisée"
#: tiramisu/option.py:571 #: tiramisu/option.py:838
msgid "max value is empty" msgid "max value is empty"
msgstr "valeur maximum est vide" msgstr "la valeur maximum est vide"
#: tiramisu/option.py:608 #: tiramisu/option.py:877
msgid "network mustn't not be in reserved class" msgid "invalid network address {0}"
msgstr "réseau ne doit pas être dans la classe reservée" msgstr "adresse réseau invalide {0}"
#: tiramisu/option.py:640 #: tiramisu/option.py:882
msgid "network shall not be in reserved class"
msgstr "le réseau ne doit pas être dans la classe reservée"
#: tiramisu/option.py:894
msgid "invalid netmask address {0}"
msgstr "masque de sous-réseau invalide {0}"
#: tiramisu/option.py:910
msgid "invalid len for opts"
msgstr "longueur invalide pour opts"
#: tiramisu/option.py:922
msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP"
msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP"
#: tiramisu/option.py:645 #: tiramisu/option.py:927
msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network"
msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau" msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau"
#: tiramisu/option.py:650 #: tiramisu/option.py:932
msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgid "invalid IP {0} ({1}) with netmask {2} ({3})"
msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" msgstr "IP invalide {0} ({1}) avec masque {2} ({3})"
#: tiramisu/option.py:652 #: tiramisu/option.py:934
msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgid "invalid network {0} ({1}) with netmask {2} ({3})"
msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})"
#: tiramisu/option.py:672 #: tiramisu/option.py:948
msgid "invalid broadcast address {0}"
msgstr "adresse de broadcast invalide {0}"
#: tiramisu/option.py:952
msgid "invalid len for vals"
msgstr "longueur invalide pour vals"
#: tiramisu/option.py:957
msgid ""
"invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})"
msgstr ""
"Broadcast invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})"
#: tiramisu/option.py:979
msgid "unknown type_ {0} for hostname" msgid "unknown type_ {0} for hostname"
msgstr "type_ inconnu {0} pour le nom d'hôte" msgstr "type_ inconnu {0} pour le nom d'hôte"
#: tiramisu/option.py:675 #: tiramisu/option.py:982
msgid "allow_ip must be a boolean" msgid "allow_ip must be a boolean"
msgstr "allow_ip doit être un booléen" msgstr "allow_ip doit être un booléen"
#: tiramisu/option.py:704 #: tiramisu/option.py:1012
msgid "invalid value for {0}, must have dot" msgid "invalid value for {0}, must have dot"
msgstr "valeur invalide pour {0}, doit avoir un point" msgstr "valeur invalide pour {0}, doit avoir un point"
#: tiramisu/option.py:707 #: tiramisu/option.py:1015
msgid "invalid domainname's length for {0} (max {1})" msgid "invalid domainname's length for {0} (max {1})"
msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})"
#: tiramisu/option.py:710 #: tiramisu/option.py:1018
msgid "invalid domainname's length for {0} (min 2)" msgid "invalid domainname's length for {0} (min 2)"
msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)"
#: tiramisu/option.py:714 #: tiramisu/option.py:1022
msgid "invalid domainname" msgid "invalid domainname"
msgstr "nom de domaine invalide" msgstr "nom de domaine invalide"
#: tiramisu/option.py:731 #: tiramisu/option.py:1049
msgid "invalid name: {0} for optiondescription"
msgstr "nom invalide : {0} pour l'optiondescription"
#: tiramisu/option.py:743
msgid "duplicate option name: {0}" msgid "duplicate option name: {0}"
msgstr "nom de l'option dupliqué : {0}" msgstr "nom de l'option dupliqué : {0}"
#: tiramisu/option.py:769 #: tiramisu/option.py:1067
msgid "unknown Option {0} in OptionDescription {1}" msgid "unknown Option {0} in OptionDescription {1}"
msgstr "Option {} inconnue pour l'OptionDescription{}" msgstr "Option {0} inconnue pour l'OptionDescription {1}"
#: tiramisu/option.py:820 #: tiramisu/option.py:1118
msgid "duplicate option: {0}" msgid "duplicate option: {0}"
msgstr "option dupliquée : {0}" msgstr "option dupliquée : {0}"
#: tiramisu/option.py:850 #: tiramisu/option.py:1148
msgid "consistency with option {0} which is not in Config"
msgstr "consistency avec l'option {0} qui n'est pas dans une Config"
#: tiramisu/option.py:1156
msgid "no option for path {0}" msgid "no option for path {0}"
msgstr "pas d'option pour le chemin {0}" msgstr "pas d'option pour le chemin {0}"
#: tiramisu/option.py:856 #: tiramisu/option.py:1162
msgid "no option {0} found" msgid "no option {0} found"
msgstr "pas d'option {0} trouvée" msgstr "pas d'option {0} trouvée"
#: tiramisu/option.py:866 #: tiramisu/option.py:1172
msgid "cannot change group_type if already set (old {0}, new {1})" msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})"
#: tiramisu/option.py:879 #: tiramisu/option.py:1185
msgid "master group {0} shall not have a subgroup" msgid "master group {0} shall not have a subgroup"
msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe"
#: tiramisu/option.py:882 #: tiramisu/option.py:1188
msgid "master group {0} shall not have a symlinkoption" msgid "master group {0} shall not have a symlinkoption"
msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption"
#: tiramisu/option.py:885 #: tiramisu/option.py:1191
msgid "not allowed option {0} in group {1}: this option is not a multi" msgid "not allowed option {0} in group {1}: this option is not a multi"
msgstr "" msgstr ""
"option non autorisée {0} dans le groupe {1} : cette option n'est pas une " "option non autorisée {0} dans le groupe {1} : cette option n'est pas une "
"multi" "multi"
#: tiramisu/option.py:896 #: tiramisu/option.py:1202
msgid "master group with wrong master name for {0}" msgid "master group with wrong master name for {0}"
msgstr "le groupe maître avec un nom de maître éroné pour {0}" msgstr "le groupe maître avec un nom de maître érroné pour {0}"
#: tiramisu/option.py:905 #: tiramisu/option.py:1211
msgid "no child has same nom has master group for: {0}" msgid "no child has same nom has master group for: {0}"
msgstr "pas d'enfant avec le nom du groupe maître pour {0} " msgstr "pas d'enfant avec le nom du groupe maître pour {0} "
#: tiramisu/option.py:908 #: tiramisu/option.py:1214
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "group_type : {0} non autorisé" msgstr "group_type : {0} non autorisé"
#: tiramisu/option.py:946 #: tiramisu/option.py:1306
msgid "malformed requirements type for option: {0}, must be a dict" msgid "malformed requirements type for option: {0}, must be a dict"
msgstr "" msgstr ""
"type requirements malformé pour l'option : {0}, doit être un dictionnaire" "type requirements malformé pour l'option : {0}, doit être un dictionnaire"
#: tiramisu/option.py:962 #: tiramisu/option.py:1323
msgid "" msgid ""
"malformed requirements for option: {0} require must have option, expected " "malformed requirements for option: {0} require must have option, expected "
"and action keys" "and action keys"
msgstr "" msgstr ""
"requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs "
"option, exptected et action" "option, expected et action"
#: tiramisu/option.py:967 #: tiramisu/option.py:1328
msgid "malformed requirements for option: {0} inverse must be boolean" msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr "requirements malformé pour l'option : {0} inverse doit être un booléen" msgstr ""
"requirements mal formés pour l'option : {0} inverse doit être un booléen"
#: tiramisu/option.py:971 #: tiramisu/option.py:1332
msgid "malformed requirements for option: {0} transitive must be boolean" msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr "requirements malformé pour l'option : {0} transitive doit être booléen" msgstr ""
"requirements mal formés pour l'option : {0} transitive doit être booléen"
#: tiramisu/option.py:975 #: tiramisu/option.py:1336
msgid "malformed requirements for option: {0} same_action must be boolean" msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr "" msgstr ""
"requirements malformé pour l'option : {0} same_action doit être un booléen" "requirements mal formés pour l'option : {0} same_action doit être un booléen"
#: tiramisu/option.py:979 #: tiramisu/option.py:1340
msgid "malformed requirements must be an option in option {0}" msgid "malformed requirements must be an option in option {0}"
msgstr "requirements malformé doit être une option dans l'option {0}" msgstr "requirements mal formés doit être une option dans l'option {0}"
#: tiramisu/option.py:982 #: tiramisu/option.py:1343
msgid "malformed requirements option {0} should not be a multi" msgid "malformed requirements option {0} should not be a multi"
msgstr "requirements malformé l'option {0} ne doit pas être une multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi"
#: tiramisu/option.py:988 #: tiramisu/option.py:1349
msgid "" msgid ""
"malformed requirements second argument must be valid for option {0}: {1}" "malformed requirements second argument must be valid for option {0}: {1}"
msgstr "" msgstr ""
"requirements malformé deuxième argument doit être valide pour l'option {0} : " "requirements mal formés deuxième argument doit être valide pour l'option "
"{1}" "{0} : {1}"
#: tiramisu/option.py:993 #: tiramisu/option.py:1354
msgid "inconsistency in action types for option: {0} action: {1}" msgid "inconsistency in action types for option: {0} action: {1}"
msgstr "incohérence dans les types action pour l'option : {0} action {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}"
#: tiramisu/setting.py:47 #: tiramisu/option.py:1379
msgid "storage_type is already set, cannot rebind it" msgid "{0} should be a function"
msgstr "storage_type est déjà défini, impossible de le redéfinir" msgstr "{0} doit être une fonction"
#: tiramisu/setting.py:67 #: tiramisu/option.py:1382
msgid "{0}_params should be a dict"
msgstr "{0}_params devrait être un dict"
#: tiramisu/option.py:1385
msgid "{0}_params with key {1} should not have length different to 1"
msgstr ""
"{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1"
#: tiramisu/option.py:1389
msgid "{0}_params should be tuple for key \"{1}\""
msgstr "{0}_params devrait être un tuple pour la clef \"{1}\""
#: tiramisu/option.py:1395
msgid "validator not support tuple"
msgstr "validator n'accepte pas de tuple"
#: tiramisu/option.py:1398
msgid "{0}_params should have an option not a {0} for first argument"
msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument"
#: tiramisu/option.py:1402
msgid "{0}_params should have a boolean not a {0} for second argument"
msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument"
#: tiramisu/setting.py:111
msgid "can't rebind {0}" msgid "can't rebind {0}"
msgstr "ne peut redéfinir ({0})" msgstr "ne peut redéfinir ({0})"
#: tiramisu/setting.py:72 #: tiramisu/setting.py:116
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "ne peut supprimer ({0})" msgstr "ne peut supprimer ({0})"
#: tiramisu/setting.py:185 #: tiramisu/setting.py:254
msgid "cannot append {0} property for option {1}: this property is calculated" msgid "cannot append {0} property for option {1}: this property is calculated"
msgstr "" msgstr ""
"ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est " "ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est "
"calculée" "calculée"
#: tiramisu/setting.py:215 #: tiramisu/setting.py:317
msgid "option {0} not already exists in storage {1}"
msgstr "option {0} n'existe pas dans l'espace de stockage {1}"
#: tiramisu/setting.py:282
msgid "opt and all_properties must not be set together in reset" msgid "opt and all_properties must not be set together in reset"
msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset" msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset"
#: tiramisu/setting.py:297 #: tiramisu/setting.py:332
msgid "if opt is not None, path should not be None in _getproperties" msgid "if opt is not None, path should not be None in _getproperties"
msgstr "" msgstr ""
"si opt n'est pas None, path devrait ne pas être à None dans _getproperties" "si opt n'est pas None, path devrait ne pas être à None dans _getproperties"
#: tiramisu/setting.py:391 #: tiramisu/setting.py:435
msgid "cannot change the value for option {0} this option is frozen" msgid "cannot change the value for option {0} this option is frozen"
msgstr "" msgstr ""
"ne peut modifié la valeur de l'option {0} cette option n'est pas modifiable" "ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable"
#: tiramisu/setting.py:397 #: tiramisu/setting.py:441
msgid "trying to access to an option named: {0} with properties {1}" msgid "trying to access to an option named: {0} with properties {1}"
msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}" msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}"
#: tiramisu/setting.py:415 #: tiramisu/setting.py:459
msgid "permissive must be a tuple" msgid "permissive must be a tuple"
msgstr "permissive doit être un tuple" msgstr "permissive doit être un tuple"
#: tiramisu/setting.py:422 tiramisu/value.py:277 #: tiramisu/setting.py:466 tiramisu/value.py:299
msgid "invalid generic owner {0}" msgid "invalid generic owner {0}"
msgstr "invalide owner générique {0}" msgstr "invalide owner générique {0}"
#: tiramisu/setting.py:503 #: tiramisu/setting.py:553
msgid "" msgid ""
"malformed requirements imbrication detected for option: '{0}' with " "malformed requirements imbrication detected for option: '{0}' with "
"requirement on: '{1}'" "requirement on: '{1}'"
msgstr "" msgstr ""
"imbrication de requirements malformé detectée pour l'option : '{0}' avec " "imbrication de requirements mal formés detectée pour l'option : '{0}' avec "
"requirement sur : '{1}'" "requirement sur : '{1}'"
#: tiramisu/setting.py:515 #: tiramisu/setting.py:565
msgid "option '{0}' has requirement's property error: {1} {2}" msgid "option '{0}' has requirement's property error: {1} {2}"
msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}" msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}"
#: tiramisu/storage/__init__.py:47
msgid "storage_type is already set, cannot rebind it"
msgstr "storage_type est déjà défini, impossible de le redéfinir"
#: tiramisu/storage/__init__.py:81
msgid "option {0} not already exists in storage {1}"
msgstr "option {0} n'existe pas dans l'espace de stockage {1}"
#: tiramisu/storage/dictionary/storage.py:37 #: tiramisu/storage/dictionary/storage.py:37
msgid "dictionary storage cannot delete session" msgid "dictionary storage cannot delete session"
msgstr "" msgstr ""
"impossible de supprimer une session dans un espace de stockage dictionary" "impossible de supprimer une session dans un espace de stockage dictionary"
#: tiramisu/storage/dictionary/storage.py:46 #: tiramisu/storage/dictionary/storage.py:48
msgid "session already used" msgid "session already used"
msgstr "session déjà utilisée" msgstr "session déjà utilisée"
#: tiramisu/storage/dictionary/storage.py:48 #: tiramisu/storage/dictionary/storage.py:50
msgid "a dictionary cannot be persistent" msgid "a dictionary cannot be persistent"
msgstr "un espace de stockage dictionary ne peut être persistant" msgstr "un espace de stockage dictionary ne peut être persistant"
#: tiramisu/value.py:284 #: tiramisu/value.py:306
msgid "no value for {0} cannot change owner to {1}" msgid "no value for {0} cannot change owner to {1}"
msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}" msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}"
#: tiramisu/value.py:356 #: tiramisu/value.py:414
msgid "invalid len for the slave: {0} which has {1} as master" msgid "invalid len for the slave: {0} which has {1} as master"
msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître" msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître"
#: tiramisu/value.py:373 #: tiramisu/value.py:438
msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgid "invalid len for the master: {0} which has {1} as slave with greater len"
msgstr "" msgstr ""
"longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus " "longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus "
"grande longueur" "grande longueur"
#: tiramisu/value.py:394 #: tiramisu/value.py:468
msgid "cannot append a value on a multi option {0} which is a slave" msgid "cannot append a value on a multi option {0} which is a slave"
msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave" msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave"
#: tiramisu/value.py:429 #: tiramisu/value.py:505
msgid "cannot sort multi option {0} if master or slave" msgid "cannot sort multi option {0} if master or slave"
msgstr "ne peut trier une option multi {0} pour une maître ou une esclave" msgstr "ne peut trier une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:433 #: tiramisu/value.py:509
msgid "cmp is not permitted in python v3 or greater" msgid "cmp is not permitted in python v3 or greater"
msgstr "cmp n'est pas permis en python v3 ou supérieure" msgstr "cmp n'est pas permis en python v3 ou supérieure"
#: tiramisu/value.py:442 #: tiramisu/value.py:518
msgid "cannot reverse multi option {0} if master or slave" msgid "cannot reverse multi option {0} if master or slave"
msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave" msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:450 #: tiramisu/value.py:526
msgid "cannot insert multi option {0} if master or slave" msgid "cannot insert multi option {0} if master or slave"
msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave" msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:458 #: tiramisu/value.py:534
msgid "cannot extend multi option {0} if master or slave" msgid "cannot extend multi option {0} if master or slave"
msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave" msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:482 #: tiramisu/value.py:562
msgid "cannot pop a value on a multi option {0} which is a slave" msgid "cannot pop a value on a multi option {0} which is a slave"
msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave"
#~ msgid "metaconfig's children must be a list" #~ msgid "invalid value {0} for option {1} which must be a list"
#~ msgstr "enfants d'une metaconfig doit être une liste" #~ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste"
#~ msgid "invalid value {0} for option {1} must be different as {2} option"
#~ msgstr ""
#~ "valeur invalide {0} pour l'option {1} doit être différente de l'option {2}"
#~ msgid "validator should return a boolean, not {0}"
#~ msgstr "le validator devrait retourner un boolean, pas un {0}"
#~ msgid "invalid value {0} for option {1} for object {2}"
#~ msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}"
#~ msgid "no config specified but needed"
#~ msgstr "aucune config spécifié alors que c'est nécessaire"
#~ msgid "{0} has no attribute impl_set_information"
#~ msgstr "{0} n'a pas d'attribut impl_set_information"
#~ msgid "{0} has no attribute impl_get_information"
#~ msgstr "{0} n'a pas d'attribut impl_get_information"
#~ msgid "invalid name: {0} for optiondescription"
#~ msgstr "nom invalide : {0} pour l'optiondescription"
#~ msgid "metaconfig's children must be config, not {0}" #~ msgid "metaconfig's children must be config, not {0}"
#~ msgstr "enfants d'une metaconfig doit être une config, pas {0}" #~ msgstr "enfants d'une metaconfig doit être une config, pas {0}"

View File

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2013-09-02 11:30+CEST\n" "POT-Creation-Date: 2013-09-28 19:06+CEST\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,395 +15,455 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: tiramisu/autolib.py:58 #: tiramisu/autolib.py:144
msgid "no config specified but needed"
msgstr ""
#: tiramisu/autolib.py:65
msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:74 #: tiramisu/autolib.py:153
msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}" msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:47 #: tiramisu/config.py:51
msgid "descr must be an optiondescription, not {0}" msgid "descr must be an optiondescription, not {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:121 #: tiramisu/config.py:126
msgid "unknown group_type: {0}" msgid "unknown group_type: {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:157 #: tiramisu/config.py:162
msgid "no option description found for this config (may be metaconfig without meta)" msgid "no option description found for this config (may be metaconfig without meta)"
msgstr "" msgstr ""
#: tiramisu/config.py:311 #: tiramisu/config.py:188
msgid "can't assign to an OptionDescription"
msgstr ""
#: tiramisu/config.py:319
msgid "unknown type_ type {0}for _find" msgid "unknown type_ type {0}for _find"
msgstr "" msgstr ""
#: tiramisu/config.py:350 #: tiramisu/config.py:358
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "" msgstr ""
#: tiramisu/config.py:400 #: tiramisu/config.py:408
msgid "make_dict can't filtering with value without option" msgid "make_dict can't filtering with value without option"
msgstr "" msgstr ""
#: tiramisu/config.py:421 #: tiramisu/config.py:429
msgid "unexpected path {0}, should start with {1}" msgid "unexpected path {0}, should start with {1}"
msgstr "" msgstr ""
#: tiramisu/config.py:481 #: tiramisu/config.py:489
msgid "opt in getowner must be an option not {0}" msgid "opt in getowner must be an option not {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:71 #: tiramisu/option.py:68
msgid "{0} has no attribute impl_set_information"
msgstr ""
#: tiramisu/option.py:86
msgid "information's item not found: {0}"
msgstr ""
#: tiramisu/option.py:89
msgid "{0} has no attribute impl_get_information"
msgstr ""
#: tiramisu/option.py:117
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
#: tiramisu/option.py:208
msgid "invalid name: {0} for option" msgid "invalid name: {0} for option"
msgstr "" msgstr ""
#: tiramisu/option.py:218 #: tiramisu/option.py:77
msgid "validator must be a function"
msgstr ""
#: tiramisu/option.py:225
msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option.py:231
msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr ""
#: tiramisu/option.py:236
msgid "default value not allowed if option: {0} is calculated"
msgstr ""
#: tiramisu/option.py:239
msgid "params defined for a callback function but no callback defined yet for option {0}"
msgstr ""
#: tiramisu/option.py:261 tiramisu/option.py:809
msgid "invalid properties type {0} for {1}, must be a tuple" msgid "invalid properties type {0} for {1}, must be a tuple"
msgstr "" msgstr ""
#: tiramisu/option.py:334 #: tiramisu/option.py:115
msgid "invalid value {0} for option {1} for object {2}" msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "" msgstr ""
#: tiramisu/option.py:342 tiramisu/value.py:468 #: tiramisu/option.py:142 tiramisu/value.py:360
msgid "information's item not found: {0}"
msgstr ""
#: tiramisu/option.py:204
msgid "cannot serialize Option, only in OptionDescription"
msgstr ""
#: tiramisu/option.py:307
msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option.py:313
msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr ""
#: tiramisu/option.py:318
msgid "default value not allowed if option: {0} is calculated"
msgstr ""
#: tiramisu/option.py:321
msgid "params defined for a callback function but no callback defined yet for option {0}"
msgstr ""
#: tiramisu/option.py:360
msgid "option not in all_cons_opts"
msgstr ""
#: tiramisu/option.py:432 tiramisu/value.py:545
msgid "invalid value {0} for option {1}: {2}" msgid "invalid value {0} for option {1}: {2}"
msgstr "" msgstr ""
#: tiramisu/option.py:354 #: tiramisu/option.py:449
msgid "invalid value {0} for option {1} which must be a list" msgid "which must be a list"
msgstr "" msgstr ""
#: tiramisu/option.py:423 #: tiramisu/option.py:509
msgid "invalid value {0} for option {1} must be different as {2} option" msgid "consistency should be set with an option"
msgstr ""
#: tiramisu/option.py:445
msgid "values must be a tuple for {0}"
msgstr ""
#: tiramisu/option.py:448
msgid "open_values must be a boolean for {0}"
msgstr ""
#: tiramisu/option.py:469
msgid "value {0} is not permitted, only {1} is allowed"
msgstr ""
#: tiramisu/option.py:481
msgid "value must be a boolean"
msgstr ""
#: tiramisu/option.py:491
msgid "value must be an integer"
msgstr ""
#: tiramisu/option.py:501
msgid "value must be a float"
msgstr "" msgstr ""
#: tiramisu/option.py:511 #: tiramisu/option.py:511
msgid "cannot add consistency with itself"
msgstr ""
#: tiramisu/option.py:513
msgid "every options in consistency should be multi or none"
msgstr ""
#: tiramisu/option.py:533
msgid "same value for {0} and {1}"
msgstr ""
#: tiramisu/option.py:642
msgid "values must be a tuple for {0}"
msgstr ""
#: tiramisu/option.py:645
msgid "open_values must be a boolean for {0}"
msgstr ""
#: tiramisu/option.py:667
msgid "value {0} is not permitted, only {1} is allowed"
msgstr ""
#: tiramisu/option.py:679
msgid "value must be a boolean"
msgstr ""
#: tiramisu/option.py:689
msgid "value must be an integer"
msgstr ""
#: tiramisu/option.py:699
msgid "value must be a float"
msgstr ""
#: tiramisu/option.py:709
msgid "value must be a string, not {0}" msgid "value must be a string, not {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:529 #: tiramisu/option.py:727
msgid "value must be an unicode" msgid "value must be an unicode"
msgstr "" msgstr ""
#: tiramisu/option.py:539 #: tiramisu/option.py:739
msgid "malformed symlinkoption must be an option for symlink {0}" msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:581 #: tiramisu/option.py:788
msgid "invalid IP {0}"
msgstr ""
#: tiramisu/option.py:793
msgid "IP mustn't not be in reserved class" msgid "IP mustn't not be in reserved class"
msgstr "" msgstr ""
#: tiramisu/option.py:583 #: tiramisu/option.py:795
msgid "IP must be in private class" msgid "IP must be in private class"
msgstr "" msgstr ""
#: tiramisu/option.py:621 #: tiramisu/option.py:833
msgid "inconsistency in allowed range" msgid "inconsistency in allowed range"
msgstr "" msgstr ""
#: tiramisu/option.py:626 #: tiramisu/option.py:838
msgid "max value is empty" msgid "max value is empty"
msgstr "" msgstr ""
#: tiramisu/option.py:663 #: tiramisu/option.py:877
msgid "network mustn't not be in reserved class" msgid "invalid network address {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:695 #: tiramisu/option.py:882
msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgid "network shall not be in reserved class"
msgstr "" msgstr ""
#: tiramisu/option.py:700 #: tiramisu/option.py:894
msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgid "invalid netmask address {0}"
msgstr ""
#: tiramisu/option.py:705
msgid "invalid IP {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:707
msgid "invalid network {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:727
msgid "unknown type_ {0} for hostname"
msgstr ""
#: tiramisu/option.py:730
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option.py:759
msgid "invalid value for {0}, must have dot"
msgstr ""
#: tiramisu/option.py:762
msgid "invalid domainname's length for {0} (max {1})"
msgstr ""
#: tiramisu/option.py:765
msgid "invalid domainname's length for {0} (min 2)"
msgstr ""
#: tiramisu/option.py:769
msgid "invalid domainname"
msgstr ""
#: tiramisu/option.py:787
msgid "invalid name: {0} for optiondescription"
msgstr ""
#: tiramisu/option.py:799
msgid "duplicate option name: {0}"
msgstr ""
#: tiramisu/option.py:825
msgid "unknown Option {0} in OptionDescription {1}"
msgstr ""
#: tiramisu/option.py:874
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option.py:904
msgid "no option for path {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:910 #: tiramisu/option.py:910
msgid "invalid len for opts"
msgstr ""
#: tiramisu/option.py:922
msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP"
msgstr ""
#: tiramisu/option.py:927
msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network"
msgstr ""
#: tiramisu/option.py:932
msgid "invalid IP {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:934
msgid "invalid network {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:948
msgid "invalid broadcast address {0}"
msgstr ""
#: tiramisu/option.py:952
msgid "invalid len for vals"
msgstr ""
#: tiramisu/option.py:957
msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})"
msgstr ""
#: tiramisu/option.py:979
msgid "unknown type_ {0} for hostname"
msgstr ""
#: tiramisu/option.py:982
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option.py:1012
msgid "invalid value for {0}, must have dot"
msgstr ""
#: tiramisu/option.py:1015
msgid "invalid domainname's length for {0} (max {1})"
msgstr ""
#: tiramisu/option.py:1018
msgid "invalid domainname's length for {0} (min 2)"
msgstr ""
#: tiramisu/option.py:1022
msgid "invalid domainname"
msgstr ""
#: tiramisu/option.py:1049
msgid "duplicate option name: {0}"
msgstr ""
#: tiramisu/option.py:1067
msgid "unknown Option {0} in OptionDescription {1}"
msgstr ""
#: tiramisu/option.py:1118
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option.py:1148
msgid "consistency with option {0} which is not in Config"
msgstr ""
#: tiramisu/option.py:1156
msgid "no option for path {0}"
msgstr ""
#: tiramisu/option.py:1162
msgid "no option {0} found" msgid "no option {0} found"
msgstr "" msgstr ""
#: tiramisu/option.py:920 #: tiramisu/option.py:1172
msgid "cannot change group_type if already set (old {0}, new {1})" msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr "" msgstr ""
#: tiramisu/option.py:933 #: tiramisu/option.py:1185
msgid "master group {0} shall not have a subgroup" msgid "master group {0} shall not have a subgroup"
msgstr "" msgstr ""
#: tiramisu/option.py:936 #: tiramisu/option.py:1188
msgid "master group {0} shall not have a symlinkoption" msgid "master group {0} shall not have a symlinkoption"
msgstr "" msgstr ""
#: tiramisu/option.py:939 #: tiramisu/option.py:1191
msgid "not allowed option {0} in group {1}: this option is not a multi" msgid "not allowed option {0} in group {1}: this option is not a multi"
msgstr "" msgstr ""
#: tiramisu/option.py:950 #: tiramisu/option.py:1202
msgid "master group with wrong master name for {0}" msgid "master group with wrong master name for {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:959 #: tiramisu/option.py:1211
msgid "no child has same nom has master group for: {0}" msgid "no child has same nom has master group for: {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:962 #: tiramisu/option.py:1214
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "" msgstr ""
#: tiramisu/option.py:1021 #: tiramisu/option.py:1306
msgid "malformed requirements type for option: {0}, must be a dict" msgid "malformed requirements type for option: {0}, must be a dict"
msgstr "" msgstr ""
#: tiramisu/option.py:1037 #: tiramisu/option.py:1323
msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgid "malformed requirements for option: {0} require must have option, expected and action keys"
msgstr "" msgstr ""
#: tiramisu/option.py:1042 #: tiramisu/option.py:1328
msgid "malformed requirements for option: {0} inverse must be boolean" msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:1046 #: tiramisu/option.py:1332
msgid "malformed requirements for option: {0} transitive must be boolean" msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:1050 #: tiramisu/option.py:1336
msgid "malformed requirements for option: {0} same_action must be boolean" msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:1054 #: tiramisu/option.py:1340
msgid "malformed requirements must be an option in option {0}" msgid "malformed requirements must be an option in option {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:1057 #: tiramisu/option.py:1343
msgid "malformed requirements option {0} should not be a multi" msgid "malformed requirements option {0} should not be a multi"
msgstr "" msgstr ""
#: tiramisu/option.py:1063 #: tiramisu/option.py:1349
msgid "malformed requirements second argument must be valid for option {0}: {1}" msgid "malformed requirements second argument must be valid for option {0}: {1}"
msgstr "" msgstr ""
#: tiramisu/option.py:1068 #: tiramisu/option.py:1354
msgid "inconsistency in action types for option: {0} action: {1}" msgid "inconsistency in action types for option: {0} action: {1}"
msgstr "" msgstr ""
#: tiramisu/setting.py:47 #: tiramisu/option.py:1379
msgid "storage_type is already set, cannot rebind it" msgid "{0} should be a function"
msgstr "" msgstr ""
#: tiramisu/setting.py:67 #: tiramisu/option.py:1382
msgid "{0}_params should be a dict"
msgstr ""
#: tiramisu/option.py:1385
msgid "{0}_params with key {1} should not have length different to 1"
msgstr ""
#: tiramisu/option.py:1389
msgid "{0}_params should be tuple for key \"{1}\""
msgstr ""
#: tiramisu/option.py:1395
msgid "validator not support tuple"
msgstr ""
#: tiramisu/option.py:1398
msgid "{0}_params should have an option not a {0} for first argument"
msgstr ""
#: tiramisu/option.py:1402
msgid "{0}_params should have a boolean not a {0} for second argument"
msgstr ""
#: tiramisu/setting.py:111
msgid "can't rebind {0}" msgid "can't rebind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:72 #: tiramisu/setting.py:116
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:185 #: tiramisu/setting.py:254
msgid "cannot append {0} property for option {1}: this property is calculated" msgid "cannot append {0} property for option {1}: this property is calculated"
msgstr "" msgstr ""
#: tiramisu/setting.py:215 #: tiramisu/setting.py:317
msgid "option {0} not already exists in storage {1}"
msgstr ""
#: tiramisu/setting.py:282
msgid "opt and all_properties must not be set together in reset" msgid "opt and all_properties must not be set together in reset"
msgstr "" msgstr ""
#: tiramisu/setting.py:297 #: tiramisu/setting.py:332
msgid "if opt is not None, path should not be None in _getproperties" msgid "if opt is not None, path should not be None in _getproperties"
msgstr "" msgstr ""
#: tiramisu/setting.py:391 #: tiramisu/setting.py:435
msgid "cannot change the value for option {0} this option is frozen" msgid "cannot change the value for option {0} this option is frozen"
msgstr "" msgstr ""
#: tiramisu/setting.py:397 #: tiramisu/setting.py:441
msgid "trying to access to an option named: {0} with properties {1}" msgid "trying to access to an option named: {0} with properties {1}"
msgstr "" msgstr ""
#: tiramisu/setting.py:415 #: tiramisu/setting.py:459
msgid "permissive must be a tuple" msgid "permissive must be a tuple"
msgstr "" msgstr ""
#: tiramisu/setting.py:422 tiramisu/value.py:277 #: tiramisu/setting.py:466 tiramisu/value.py:299
msgid "invalid generic owner {0}" msgid "invalid generic owner {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:503 #: tiramisu/setting.py:553
msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'"
msgstr "" msgstr ""
#: tiramisu/setting.py:515 #: tiramisu/setting.py:565
msgid "option '{0}' has requirement's property error: {1} {2}" msgid "option '{0}' has requirement's property error: {1} {2}"
msgstr "" msgstr ""
#: tiramisu/storage/__init__.py:47
msgid "storage_type is already set, cannot rebind it"
msgstr ""
#: tiramisu/storage/__init__.py:81
msgid "option {0} not already exists in storage {1}"
msgstr ""
#: tiramisu/storage/dictionary/storage.py:37 #: tiramisu/storage/dictionary/storage.py:37
msgid "dictionary storage cannot delete session" msgid "dictionary storage cannot delete session"
msgstr "" msgstr ""
#: tiramisu/storage/dictionary/storage.py:46 #: tiramisu/storage/dictionary/storage.py:48
msgid "session already used" msgid "session already used"
msgstr "" msgstr ""
#: tiramisu/storage/dictionary/storage.py:48 #: tiramisu/storage/dictionary/storage.py:50
msgid "a dictionary cannot be persistent" msgid "a dictionary cannot be persistent"
msgstr "" msgstr ""
#: tiramisu/value.py:284 #: tiramisu/value.py:306
msgid "no value for {0} cannot change owner to {1}" msgid "no value for {0} cannot change owner to {1}"
msgstr "" msgstr ""
#: tiramisu/value.py:356 #: tiramisu/value.py:414
msgid "invalid len for the slave: {0} which has {1} as master" msgid "invalid len for the slave: {0} which has {1} as master"
msgstr "" msgstr ""
#: tiramisu/value.py:373 #: tiramisu/value.py:438
msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgid "invalid len for the master: {0} which has {1} as slave with greater len"
msgstr "" msgstr ""
#: tiramisu/value.py:394 #: tiramisu/value.py:468
msgid "cannot append a value on a multi option {0} which is a slave" msgid "cannot append a value on a multi option {0} which is a slave"
msgstr "" msgstr ""
#: tiramisu/value.py:429 #: tiramisu/value.py:505
msgid "cannot sort multi option {0} if master or slave" msgid "cannot sort multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:433 #: tiramisu/value.py:509
msgid "cmp is not permitted in python v3 or greater" msgid "cmp is not permitted in python v3 or greater"
msgstr "" msgstr ""
#: tiramisu/value.py:442 #: tiramisu/value.py:518
msgid "cannot reverse multi option {0} if master or slave" msgid "cannot reverse multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:450 #: tiramisu/value.py:526
msgid "cannot insert multi option {0} if master or slave" msgid "cannot insert multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:458 #: tiramisu/value.py:534
msgid "cannot extend multi option {0} if master or slave" msgid "cannot extend multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:482 #: tiramisu/value.py:562
msgid "cannot pop a value on a multi option {0} which is a slave" msgid "cannot pop a value on a multi option {0} which is a slave"
msgstr "" msgstr ""