Option’s property
Let’s start to build a config.
First of, import needed object:
1from stat import S_IMODE, S_ISDIR, S_ISSOCK
2from os import lstat, getuid, getgid
3from os.path import exists
4from pwd import getpwuid
5from grp import getgrgid
6from tiramisu import FilenameOption, UsernameOption, GroupnameOption, IntOption, BoolOption, ChoiceOption, \
7 OptionDescription, Leadership, Config, Calculation, Params, ParamSelfOption, ParamOption, ParamValue, \
8 calc_value
9from tiramisu.error import LeadershipError, PropertiesOptionError
Instanciate a first option to call a file name:
1filename = FilenameOption('filename',
2 'Filename',
3 multi=True,
4 properties=('mandatory',))
Secondly add an exists option to know if this file is already created:
1exists_ = BoolOption('exists',
2 'This file exists',
3 Calculation(exists, Params(ParamOption(filename))),
4 multi=True,
5 properties=('mandatory', 'frozen', 'force_default_on_freeze', 'advanced'))
Thirdly add a create option used by a potential script to create wanted file:
1create = BoolOption('create',
2 'Create automaticly the file',
3 multi=True,
4 default_multi=True,
5 properties=(Calculation(calc_value,
6 Params(ParamValue('disabled'),
7 kwargs={'condition': ParamOption(exists_),
8 'expected': ParamValue(True)})),))
A new option is create to known the file type. If file already exists, retrieve automaticly the type, otherwise ask to the user:
1def calc_type(filename, is_exists):
2 if is_exists:
3 mode = lstat(filename).st_mode
4 if S_ISSOCK(mode):
5 return 'socket'
6 elif S_ISDIR(mode):
7 return 'directory'
8 return 'file'
1type_ = ChoiceOption('type',
2 'The file type',
3 ('file', 'directory', 'socket'),
4 Calculation(calc_type, Params((ParamOption(filename),
5 ParamOption(exists_)))),
6 multi=True,
7 properties=('force_default_on_freeze', 'mandatory',
8 Calculation(calc_value,
9 Params(ParamValue('hidden'),
10 kwargs={'condition': ParamOption(exists_),
11 'expected': ParamValue(True)})),
12 Calculation(calc_value,
13 Params(ParamValue('frozen'),
14 kwargs={'condition': ParamOption(exists_),
15 'expected': ParamValue(True)}))))
In same model, create a user and group name options:
1def get_username(filename, exists, create=False):
2 if exists:
3 uid = lstat(filename).st_uid
4 elif create:
5 # the current uid
6 uid = getuid()
7 else:
8 return
9
10 return getpwuid(uid).pw_name
11
12
13def get_grpname(filename, exists, create=False):
14 if exists:
15 gid = lstat(filename).st_gid
16 elif create:
17 # the current gid
18 gid = getgid()
19 else:
20 return
21 return getgrgid(gid).gr_name
1username = UsernameOption('user',
2 'User',
3 default_multi=Calculation(get_username, Params((ParamOption(filename),
4 ParamOption(exists_),
5 ParamOption(create, notraisepropertyerror=True)))),
6 multi=True,
7 properties=('force_store_value',
8 Calculation(calc_value,
9 Params(ParamValue('mandatory'),
10 kwargs={'condition': ParamOption(create, notraisepropertyerror=True),
11 'expected': ParamValue(True),
12 'no_condition_is_invalid': ParamValue(True)})),))
13grpname = GroupnameOption('group',
14 'Group',
15 default_multi=Calculation(get_grpname, Params((ParamOption(filename),
16 ParamOption(exists_),
17 ParamOption(create, notraisepropertyerror=True)))),
18 multi=True,
19 properties=('force_store_value',
20 Calculation(calc_value,
21 Params(ParamValue('mandatory'),
22 kwargs={'condition': ParamOption(create, notraisepropertyerror=True),
23 'expected': ParamValue(True),
24 'no_condition_is_invalid': ParamValue(True)})),))
Finally create a mode option:
1def calc_mode(filename, is_exists, type):
2 if is_exists:
3 return int(oct(S_IMODE(lstat(filename).st_mode))[2:])
4 if type == 'file':
5 return 644
6 elif type == 'directory':
7 return 755
8 elif type == 'socket':
9 return 444
1mode = IntOption('mode',
2 'Mode',
3 default_multi=Calculation(calc_mode, Params((ParamOption(filename), ParamOption(exists_), ParamOption(type_)))),
4 multi=True,
5 properties=('mandatory', 'advanced', 'force_store_value'))
Let’s build the config:
1new = Leadership('new',
2 'Add new file',
3 [filename, exists_, create, type_, username, grpname, mode])
4
5root = OptionDescription('root', 'root', [new])
6
7config = Config(root)
Get/add/pop/reset property
option description’s property
An option description is an option. It’s possible to set property to it:
>>> config.property.read_write()
>>> config.option('new').property.get()
set()
To add a property:
>>> config.option('new').property.add('disabled')
>>> config.option('new').property.get()
set('disabled')
The property affect the option description:
>>> try:
... config.option('new').value.get()
... except PropertiesOptionError as err:
... print(err)
cannot access to optiondescription "Add new file" because has property "disabled"
But, of course the child option too. If access to option description is not possible, it’s not possible to child option too:
>>> try:
... config.option('new.filename').value.get()
... except PropertiesOptionError as err:
... print(err)
cannot access to optiondescription "Add new file" because has property "disabled"
We can remove an existed property too:
>>> config.option('new').property.add('hidden')
>>> config.option('new').property.get()
{'hidden', 'disabled'}
>>> config.option('new').property.pop('hidden')
>>> config.option('new').property.get()
{'disabled'}
It’s possible to reset property:
>>> config.option('new').property.reset()
>>> config.option('new').value.get()
{'filename': [], 'exists': [], 'create': [], 'type': [], 'user': [], 'group': [], 'mode': []}
option’s property
In a simple option we can add, pop or reset property:
>>> config.property.read_write()
>>> config.option('new.filename').property.get())
{'mandatory', 'unique', 'empty'}
>>> config.option('new.filename').property.add('frozen')
>>> config.option('new.filename').property.get()
{'mandatory', 'unique', 'frozen', 'empty'}
>>> config.option('new.filename').property.pop('empty')
>>> config.option('new.filename').property.get()
{'frozen', 'mandatory', 'unique'}
>>> config.option('new.filename').property.reset()
>>> config.option('new.filename').property.get()
{'mandatory', 'unique', 'empty'}
leader’s property
In leader’s option can only have a list of property. For other’s property, please set directly in leadership option:
>>> config.property.read_write()
>>> try:
... config.option('new.filename').property.add('hidden')
... except LeadershipError as err:
... print(err)
leader cannot have "hidden" property
>>> config.option('new').property.add('hidden')
This hidden property has to affect leader option but also all follower option.
That why you have to set this kind of properties directly in leadership option.
Note
Allowed properties for a leader: ‘empty’, ‘unique’, ‘force_store_value’, ‘mandatory’, ‘force_default_on_freeze’, ‘force_metaconfig_on_freeze’, and ‘frozen’.
follower’s property
First of add, add values in leader option:
>>> config.property.read_write()
>>> config.option('new.filename').value.set(['/etc/passwd', 'unknown1', 'unknown2'])
We have to get property with an index:
>>> config.option('new.create', 1).property.get()
set()
We can set property with index:
>>> config.option('new.create', 1).property.add('frozen')
>>> config.option('new.create', 1).property.get()
{'frozen'}
>>> config.option('new.create', 2).property.get()
set()
But we can alse set without index (available for all follower’s value):
>>> config.option('new.create').property.add('frozen')
>>> print(config.option('new.create', 1).property.get())
{'frozen'}
>>> print(config.option('new.create', 2).property.get())
{'frozen'}
Calculated property
A property can be a Calculation. That means that the property will be set or not following the context.
The Calculation can return two type of value:
a
strthis string is a new propertyNoneso this property is cancel
First of all, have a look to the create properties:
1 default_multi=True,
2 properties=(Calculation(calc_value,
3 Params(ParamValue('disabled'),
4 kwargs={'condition': ParamOption(exists_),
5 'expected': ParamValue(True)})),))
This option has only one property which is disabled when exists has value True.
If the file exists, we don’t have to now if user wants create it. It is already exists. So we don’t have to access to this option.
Secondly, have a look to the type properties:
1 properties=('force_default_on_freeze', 'mandatory',
2 Calculation(calc_value,
3 Params(ParamValue('hidden'),
4 kwargs={'condition': ParamOption(exists_),
5 'expected': ParamValue(True)})),
6 Calculation(calc_value,
7 Params(ParamValue('frozen'),
8 kwargs={'condition': ParamOption(exists_),
9 'expected': ParamValue(True)}))))
There is:
two static properties:
force_default_on_freezeandmandatory.two calculated properties:
hiddenandfrozen
If the file is already exists, the two calculated properties are present to this option.
So we can access to this option only in read only mode and user cannot modified it’s value.
Finally have a look to the username and grpname options’ properties:
1 properties=('force_store_value',
2 Calculation(calc_value,
3 Params(ParamValue('mandatory'),
4 kwargs={'condition': ParamOption(create, notraisepropertyerror=True),
5 'expected': ParamValue(True),
6 'no_condition_is_invalid': ParamValue(True)})),))
In this case we have two properties:
one static property:
force_store_valueone calculated property:
mandatory
This calculated property is apply only if create is True.
Be carefull to the create option. It could be disabled, so not accessible in calculation if the file exists as see previously.
That why we add notraisepropertyerror attribute to True, even if the calculation will failed.
In this case the value of create is not add in calc_value argument.
In this case the function calc_value consider that the property mandatory has to be set.
But we just want to set mandatory property only if create is False. That why we add the no_condition_is_invalid to True.
Force the registration of a value
The property force_store_value is a special property. This property permit to store a value automaticly even if user do not set value or reset the value.
This is useful especially, for example, for recording a random draw password through a calculation. Or to store any first result for a calculation.
To the, create a new config:
>>> config = Config(root)
If we add value in filename, the option exists stay a default value, but not the mode option, which has force_store_value:
>>> config.property.read_write()
>>> config.option('new.filename').value.set(['/etc'])
>>> print(config.option('new.filename').owner.get())
user
>>> print(config.option('new.exists', 0).owner.get())
default
>>> print(config.option('new.mode', 0).owner.get())
forced
If we try to reset mode value, this option is modified:
>>> config.option('new.mode', 0).value.reset()
>>> config.option('new.mode', 0).owner.get()
forced
Non-empty value, mandatory and unique
Leader and multi have automaticly two properties unique and empty:
>>> config = Config(OptionDescription('root', 'root', [FilenameOption('filename',
... 'Filename',
... multi=True)]))
>>> config.option('filename').property.get()
{'empty', 'unique'}
To remove empty property
>>> config = Config(OptionDescription('root', 'root', [FilenameOption('filename',
... 'Filename',
... properties=('notempty',),
... multi=True)]))
>>> config.option('filename').property.get()
{'unique'}
>>> config = Config(OptionDescription('root', 'root', [FilenameOption('filename',
... 'Filename',
... properties=('notunique',),
... multi=True)]))
>>> config.option('filename').property.get()
{'empty'}
Let’s try with previous config.
First of all we remove force_store_value mode:
>>> config = Config(root)
>>> properties = config.property.getdefault('read_write', 'append') - {'force_store_value'}
>>> config.property.setdefault(frozenset(properties), 'read_write', 'append')
>>> properties = config.property.getdefault('read_only', 'append') - {'force_store_value'}
>>> config.property.setdefault(frozenset(properties), 'read_only', 'append')
In addition to the specified mandatory property, leader have automaticly two properties: unique and empty:
>>> config.option('new.filename').property.get()
{'unique', 'mandatory', 'empty'}
What is the difference between the property unique and mandatory?
Let’s try with no value at all:
>>> config.property.read_only()
>>> try:
... config.option('new.filename').value.get()
>>> except PropertiesOptionError as err:
... print(err)
cannot access to option "Filename" because has property "mandatory"
A mandatory multi must have at least one value. This value is check only in read only mode.
If we remove the mandatory property, the value is valid:
>>> config.property.read_write()
>>> config.option('new.filename').property.pop('mandatory')
>>> config.option('new.filename').property.get()
{'unique', 'empty'}
>>> config.property.read_only()
>>> config.option('new.filename').value.get()
[]
A empty multi can has no value, but if you set a value, it must not be None:
>>> config.property.read_write()
>>> config.option('new.filename').value.set(['/etc', None])
>>> config.property.read_only()
>>> try:
... config.option('new.filename').value.get()
... except PropertiesOptionError as err:
... print(err)
cannot access to option "Filename" because has property "empty"
Trying now without this property:
>>> config.property.read_write()
>>> config.option('new.filename').property.pop('empty')
>>> config.option('new.filename').value.set(['/etc', None])
>>> config.property.read_only()
>>> config.option('new.filename').value.get()
['/etc', None]
A unique property in multi means you cannot have same value twice:
>>> config.property.read_write()
>>> try:
... config.option('new.filename').value.set(['/etc', '/etc'])
... except ValueError as err:
... print(err)
"['/etc', '/etc']" is an invalid file name for "Filename", the value "/etc" is not unique
When removing this property:
>>> config.property.read_write()
>>> config.option('new.filename').property.pop('unique')
>>> config.option('new.filename').value.set(['/etc', '/etc'])
>>> config.property.read_only()
>>> config.option('new.filename').value.get()
['/etc', '/etc']
Non-modifiable option
Freeze an option means that you cannot change the value of this option:
>>> config = Config(root)
>>> config.property.read_write()
>>> config.option('new.filename').value.set(['unknown'])
>>> config.option('new.create', 0).value.set(False)
>>> config.option('new.create', 0).property.add('frozen')
>>> try:
... config.option('new.create', 0).value.set(False)
... except PropertiesOptionError as err:
... print(err)
cannot modify the option "Create automaticly the file" because has property "frozen"
Sometime (for example when an option is calculated) we want retrieve the default value (so the calculated value) when we add frozen option.
In the current example, new.exists is a calculated value and we don’t want that the used modify this option. So we add frozen and force_default_on_freeze properties.
For example, without mode, we can modify the new.exists option, but in read_only mode, we want to have default value:
>>> config = Config(root)
>>> config.option('new.filename').value.set(['unknown'])
>>> config.option('new.exists', 0).value.set(True)
>>> config.option('new.exists', 0).value.get()
True
>>> config.property.read_write()
>>> config.option('new.exists', 0).value.get()
False
The property force_default_on_freeze is also avalaible in the option new.type. If the file exists, the type is calculated but if it not already exists, the user needs to set the correct wanted type.