Compare commits

..

1 commit

Author SHA1 Message Date
a6eacf471e feat: dynoption 2024-02-20 21:20:23 +01:00
90 changed files with 11445 additions and 14913 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
*~ *~
*# *#
*.pyc *.pyc
*.mo
*.swp *.swp
build/ build/

View file

@ -1,40 +0,0 @@
## 5.1.0rc0 (2024-11-06)
### Feat
- add min_len, max_len, forbidden_char for password option
## 5.0.0 (2024-11-01)
### Fix
- add changelog_merge_prerelease to commitizen
## 5.0.0rc0 (2024-11-01)
### Feat
- return propertyerror in function
- mandatory for a variable and replace suffix to identifier
- reorganise code
### Fix
- translation + black
- regexp tests
- update documentation
- better permissive support
- 4.1.0
## 4.0.2 (2024-02-20)
### Fix
- sub dynamic in sub dynamic family
## 4.0.1 (2023-12-17)
### Feat
- documentation
- dynamic family can have sub family

8
MANIFEST.in Normal file
View file

@ -0,0 +1,8 @@
# Include the README
include *.rst
# Include the license file
include LICENSE.txt
# Include the data files
recursive-include tiramisu *.py *.mo

View file

@ -1,25 +1,13 @@
![Logo Tiramisu](logo.png "logo Tiramisu") ![Logo Tiramisu](logo.png "logo Tiramisu")
An options controller tool
-------------------------------------
Due to more and more available options required to set up an operating system,
compiler options or whatever, it became quite annoying to hand the necessary
options to where they are actually used and even more annoying to add new
options. To circumvent these problems the configuration control was
introduced...
Tiramisu is an options handler and an options controller, wich aims at
producing flexible and fast options access.
[Documentations](doc/README.md) [Documentations](doc/README.md)
# LICENSES # LICENSES
See [COPYING](COPYING) for the licences of the code and the documentation. See COPYING for the licences of the code and the documentation.
See [AUTHORS](AUTHORS) for the details about the tiramisu's team. See AUTHORS for the details about the tiramisu's team.

View file

@ -83,20 +83,20 @@ Here is how to print our :class:`Config` details:
Root config object that enables us to handle the configuration options Root config object that enables us to handle the configuration options
Settings:
forcepermissive Access to option without verifying permissive properties
unrestraint Access to option without property restriction
nowarnings Do not warnings during validation
Commands: Commands:
description Get option description cache Manage config cache
dict Convert config and option to tiramisu format config Actions to Config
get Get Tiramisu option information Manage config informations
has_dependency Test if option has dependency option Select an option
isdynamic Test if option is a dynamic optiondescription owner Global owner
isleadership Test if option is a leader or a follower permissive Manage config permissives
isoptiondescription Test if option is an optiondescription property Manage config properties
list List options (by default list only option) value Manage config value
name Get option name
option Select an option by path
path Get option path
type Get de option type
updates Updates value with tiramisu format
Then let's print our :class:`Option` details. Then let's print our :class:`Option` details.
@ -105,35 +105,16 @@ Then let's print our :class:`Option` details.
.. code-block:: bash .. code-block:: bash
Manage selected option Select an option
Call: Select an option by path
Commands: Commands:
dependencies Get dependencies from this option dict Convert config and option to tiramisu format
description Get option description find Find an or a list of options
dict Convert config and option to tiramisu format list List options (by default list only option)
extra Get de option extra name get the name
followers Get the followers option for a leadership updates Updates value with tiramisu format
get Get Tiramisu option
group_type Get type for an optiondescription (only for optiondescription)
has_dependency Test if option has dependency
identifiers Get identifiers for dynamic option
index Get index of option
isdynamic Test if option is a dynamic optiondescription
isfollower Test if option is a follower
isleader Test if option is a leader
isleadership Test if option is a leader or a follower
ismulti Test if option could have multi value
isoptiondescription Test if option is an optiondescription
issubmulti Test if option could have submulti value
issymlinkoption Test if option is a symlink option
leader Get the leader option for a leadership or a follower option
list List options inside an option description (by default list only option)
name Get option name
option For OptionDescription get sub option, for symlinkoption get the linked option
path Get option path
pattern Get the option pattern
type Get de option type
updates Updates value with tiramisu format
Finaly, let's print the :class:`Config`. Finaly, let's print the :class:`Config`.
@ -142,7 +123,7 @@ Finaly, let's print the :class:`Config`.
.. code-block:: bash .. code-block:: bash
<Config path=None> <tiramisu.api.Config object at 0x7f3ee6204278>
:download:`download the getting started code <src/getting_started.py>` :download:`download the getting started code <src/getting_started.py>`

View file

@ -25,32 +25,26 @@ Dynamic option description is an :class:`OptionDescription` which multiplies acc
Note:: the option can be an :doc:`option` or an other option description Note:: the option can be an :doc:`option` or an other option description
* - identifiers * - suffixes
- Identifiers is a :doc:`calculation` that return the list of identifiers used to create dynamic option description. - Suffixes is a :doc:`calculation` that return the list of suffixes used to create dynamic option description.
* - properties * - properties
- A list of :doc:`property` (inside a frozenset(). - A list of :doc:`property` (inside a frozenset().
* - informations
- We can add default informations to this option description.
* - group_type
- Type for this group.
Example Example
============== ==============
Let's try: Let's try:
>>> from tiramisu import StrOption, DynOptionDescription, Calculation >>> from tiramisu import StrOption, DynOptionDescription, Calculation
>>> def return_identifiers(): >>> def return_suffixes():
... return ['1', '2'] ... return ['1', '2']
>>> child1 = StrOption('first', 'First basic option ') >>> child1 = StrOption('first', 'First basic option ')
>>> child2 = StrOption('second', 'Second basic option ') >>> child2 = StrOption('second', 'Second basic option ')
>>> DynOptionDescription('basic ', >>> DynOptionDescription('basic ',
... 'Basic options ', ... 'Basic options ',
... [child1, child2], ... [child1, child2],
... Calculation(return_identifiers)) ... Calculation(return_suffixes))
This example will construct: This example will construct:

View file

@ -35,9 +35,6 @@ The leadership
* - properties * - properties
- A list of :doc:`property` (inside a frozenset(). - A list of :doc:`property` (inside a frozenset().
* - informations
- We can add default informations to this option description.
Example Example
==================== ====================

View file

@ -18,6 +18,9 @@ Option
* - doc * - doc
- The `description` allows the user to understand where this option will be used for. - The `description` allows the user to understand where this option will be used for.
* - multi
- There are cases where it can be interesting to have a list of values rather than just one.
* - default * - default
- For each option, we can defined a default value. This value will be the value of this option until user customize it. - For each option, we can defined a default value. This value will be the value of this option until user customize it.
@ -32,20 +35,14 @@ Option
The default_multi value can be a :doc:`calculation`. The default_multi value can be a :doc:`calculation`.
* - multi
- There are cases where it can be interesting to have a list of values rather than just one.
* - validators * - validators
- A list of :doc:`validator`. - A list of :doc:`validator`.
* - properties
- A list of :doc:`property` (inside a frozenset().
* - warnings_only * - warnings_only
- Only emit warnings if not type validation is invalid. - Only emit warnings if not type validation is invalid.
* - informations * - properties
- We can add default informations to this option. - A list of :doc:`property` (inside a frozenset().
Examples Examples

View file

@ -25,12 +25,6 @@ Option description
* - properties * - properties
- A list of :doc:`property` (inside a frozenset(). - A list of :doc:`property` (inside a frozenset().
* - informations
- We can add default informations to this option description.
* - group_type
- Type for this group.
Examples Examples
============== ==============

View file

@ -103,7 +103,7 @@ Network options
- -
* - BroadcastOption * - BroadcastOption
- The last address within a network broadcast transmission to all hosts on the link. This option allow you to enter a broadcast. - The last address within a network broadcast transmission to all hosts on the link. This option allow you to enter a broadcast:
- -
* - PortOption * - PortOption
@ -114,10 +114,6 @@ Network options
- allow_wellknown: by default, the well-known ports (also known as system ports) those from 1 through 1023 are allowed, you can disabled it - allow_wellknown: by default, the well-known ports (also known as system ports) those from 1 through 1023 are allowed, you can disabled it
- allow_registred: by default, the registered ports are those from 1024 through 49151 are allowed, you can disabled it - allow_registred: by default, the registered ports are those from 1024 through 49151 are allowed, you can disabled it
- allow_private: allow dynamic or private ports, which are those from 49152 through 65535, one common use for this range is for ephemeral ports - allow_private: allow dynamic or private ports, which are those from 49152 through 65535, one common use for this range is for ephemeral ports
- allow_protocol: allow to define protocol in value, it should be something like tcp:80 or udp:53
* - MACOption
- MAC address for a network card.
Examples Examples
@ -280,35 +276,18 @@ Unix options
* - Type * - Type
- Comments - Comments
- Extra parameters
* - UsernameOption * - UsernameOption
- An unix username option is a 32 characters maximum length with lowercase ASCII characters, number, '_' or '-'. The username have to start with lowercase ASCII characters or "_". - An unix username option is a 32 characters maximum length with lowercase ASCII characters, number, '_' or '-'. The username have to start with lowercase ASCII characters or "_".
-
* - GroupnameOption * - GroupnameOption
- Same conditions has username - Same conditions has username
-
* - PasswordOption * - PasswordOption
- Simple string with no other restriction: - Simple string with no other restriction:
-
- min_len: minimum length autorise for a password
- max_len: maximum length autorise for a passwword
- forbidden_char: list of forbidden characters for a password
* - FilenameOption * - FilenameOption
- For this option, only lowercase and uppercas ASCII character, "-", ".", "_", "~", and "/" are allowed. - For this option, only lowercase and uppercas ASCII character, "-", ".", "_", "~", and "/" are allowed.
-
- allow_relative: filename should starts with "/" (something like /etc/passwd), we can, with this option to allow relative name
- test_existence: file or directory should exists
- types
- file: it should be a file
- directory: it should be a directory
* - PermissionsOption
- Permissions for Unix file. It could be something like 644 or 1644.
-
>>> from tiramisu import UsernameOption >>> from tiramisu import UsernameOption
>>> UsernameOption('user', 'user', 'my_user') >>> UsernameOption('user', 'user', 'my_user')

View file

@ -14,6 +14,6 @@ cfg = Config(descr)
# the global help about the config # the global help about the config
cfg.help() cfg.help()
# help about an option # help about an option
cfg.option("bool").help() cfg.option.help()
# the config's __repr__ # the config's __repr__
print(cfg) print(cfg)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,47 +0,0 @@
[build-system]
build-backend = "flit_core.buildapi"
requires = ["flit_core >=3.8.0,<4"]
[project]
name = "tiramisu"
version = "5.2.0"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "an options controller tool"
requires-python = ">=3.10"
license = {file = "LICENSE"}
classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
]
dependencies = [
]
[tool.flit.sdist]
exclude = [
]
[project.urls]
Home = "https://forge.cloud.silique.fr/stove/tiramisu"
[tool.commitizen]
name = "cz_conventional_commits"
ag_format = "$version"
version_scheme = "pep440"
version_provider = "pep621"
version_files = [
"tiramisu/__version__.py",
"pyproject.toml:version"
]
#update_changelog_on_bump = true
changelog_merge_prerelease = true

52
setup.py Normal file
View file

@ -0,0 +1,52 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup
import os
from tiramisu import __version__
ORI_PACKAGE_NAME = 'tiramisu'
PACKAGE_NAME = os.environ.get('PACKAGE_DST', ORI_PACKAGE_NAME)
if PACKAGE_NAME != ORI_PACKAGE_NAME:
package_dir = {PACKAGE_NAME: ORI_PACKAGE_NAME}
else:
package_dir = None
setup(
version=__version__,
author="Tiramisu's team",
author_email='gnunux@gnunux.info',
name=PACKAGE_NAME,
description='an options controller tool',
url='https://framagit.org/tiramisu/tiramisu',
license='GNU Library or Lesser General Public License (LGPL)',
provides=['tiramisu_api'],
install_requires=['setuptools'],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Natural Language :: English",
"Natural Language :: French",
],
long_description="""\
An options controller tool
-------------------------------------
Due to more and more available options required to set up an operating system,
compiler options or whatever, it became quite annoying to hand the necessary
options to where they are actually used and even more annoying to add new
options. To circumvent these problems the configuration control was
introduced...
Tiramisu is an options handler and an options controller, wich aims at
producing flexible and fast options access.
This version requires Python 3.5 or later.
""",
include_package_data=True,
package_dir=package_dir,
packages=[PACKAGE_NAME],
)

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
# from json import dumps, loads # from json import dumps, loads
import asyncio
from os import environ from os import environ
try: try:
from tiramisu_api import Config from tiramisu_api import Config
@ -41,35 +42,3 @@ def global_owner(config, config_type):
@pytest.fixture(params=PARAMS) @pytest.fixture(params=PARAMS)
def config_type(request): def config_type(request):
return request.param return request.param
def parse_od_get(dico):
ret = {}
for k, v in dico.items():
if k.isoptiondescription():
if k.isleadership():
leader_path = k.leader().path()
ret_leadership = []
for variable, value in v.items():
if variable.path() == leader_path:
for val in value:
ret_leadership.append({leader_path: val})
else:
ret_leadership[variable.index()][variable.path()] = value
ret[leader_path] = ret_leadership
else:
ret.update(parse_od_get(v))
else:
ret[k.path()] = v
return ret
def get_dependencies(option):
ret = []
for a in option.dependencies():
if a.isoptiondescription():
ret.append((a.path(), None))
else:
ret.append((a.path(), a.index()))
ret.sort()
return ret

View file

@ -0,0 +1,3 @@
{
"options.unicode": null
}

View file

@ -32,9 +32,6 @@ def list_data(ext='.py'):
for filename in filenames: for filename in filenames:
# if filename.endswith(ext) and not filename.startswith('__'): # if filename.endswith(ext) and not filename.startswith('__'):
if filename.endswith(ext) and not filename.startswith('__') and not 'not_equal' in filename and not 'callback' in filename and not filename == 'unicode2_copy.py' and not filename == 'unicode2_multi_copy.py': if filename.endswith(ext) and not filename.startswith('__') and not 'not_equal' in filename and not 'callback' in filename and not filename == 'unicode2_copy.py' and not filename == 'unicode2_multi_copy.py':
# if 'leadership' in filename:
# print('FIXME')
# continue
ret.append(filename) ret.append(filename)
return ret return ret
@ -61,7 +58,7 @@ def load_config(filename,
form.extend(mod.get_form(add_extra_od)) form.extend(mod.get_form(add_extra_od))
config.property.read_write() config.property.read_write()
if root is None: if root is None:
values = loads(dumps(config.dict(remotable=remote, clearable=clearable, form=form))) values = loads(dumps(config.option.dict(remotable=remote, clearable=clearable, form=form)))
else: else:
values = loads(dumps(config.option(root).dict(remotable=remote, clearable=clearable, form=form))) values = loads(dumps(config.option(root).dict(remotable=remote, clearable=clearable, form=form)))
return values return values
@ -383,12 +380,11 @@ def test_updates(filename_mod):
if dico_ori is None: if dico_ori is None:
if clearable == 'minimum' and remote == 'minimum': if clearable == 'minimum' and remote == 'minimum':
with open(join(datadir, modulepath + '.dict'), 'w') as fh: with open(join(datadir, modulepath + '.dict'), 'w') as fh:
pouet dump(config.value.dict(), fh, indent=2)
dump(config.value.get(), fh, indent=2)
else: else:
assert config.value.get() == dico_ori, "clearable {}, remote: {}, filename: {}".format(clearable, remote, filename_mod) assert config.value.dict() == dico_ori, "clearable {}, remote: {}, filename: {}".format(clearable, remote, filename_mod)
if root is None: if root is None:
suboption = config suboption = config.option
else: else:
suboption = config.option(root) suboption = config.option(root)
if with_model: if with_model:

View file

@ -54,13 +54,12 @@ def test_cache_importation():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.option('u2').value.set(1) cfg.option('u2').value.set(1)
values = cfg._config_bag.context._impl_values_cache
export = cfg.value.exportation() export = cfg.value.exportation()
compare(values.get_cached(), {'u2': {None: (1, None)}}) assert cfg.value.dict() == {'u1': [], 'u2': 1, 'u3': []}
cfg.option('u2').value.set(2) cfg.option('u2').value.set(2)
compare(values.get_cached(), {'u2': {None: (2, None)}}) assert cfg.value.dict() == {'u1': [], 'u2': 2, 'u3': []}
cfg.value.importation(export) cfg.value.importation(export)
compare(values.get_cached(), {}) assert cfg.value.dict() == {'u1': [], 'u2': 1, 'u3': []}
# assert not list_sessions() # assert not list_sessions()
@ -69,11 +68,11 @@ def test_cache_importation_property():
cfg = Config(od1) cfg = Config(od1)
cfg.option('u2').property.add('prop') cfg.option('u2').property.add('prop')
export = cfg.property.exportation() export = cfg.property.exportation()
assert cfg.option('u2').property.get() == {'validator', 'prop'} assert cfg.option('u2').property.get() == {'prop'}
cfg.option('u2').property.add('prop2') cfg.option('u2').property.add('prop2')
assert cfg.option('u2').property.get() == {'validator', 'prop', 'prop2'} assert cfg.option('u2').property.get() == {'prop', 'prop2'}
cfg.property.importation(export) cfg.property.importation(export)
assert cfg.option('u2').property.get() == {'validator', 'prop'} assert cfg.option('u2').property.get() == {'prop'}
cfg = Config(od1) cfg = Config(od1)
# assert not list_sessions() # assert not list_sessions()
@ -81,10 +80,10 @@ def test_cache_importation_property():
def test_cache_importation_permissive(): def test_cache_importation_permissive():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.option('u2').permissive.add('prop') cfg.option('u2').permissive.set(frozenset(['prop']))
export = cfg.permissive.exportation() export = cfg.permissive.exportation()
assert cfg.option('u2').permissive.get() == {'prop'} assert cfg.option('u2').permissive.get() == {'prop'}
cfg.option('u2').permissive.add('prop2') cfg.option('u2').permissive.set(frozenset(['prop', 'prop2']))
assert cfg.option('u2').permissive.get() == {'prop', 'prop2'} assert cfg.option('u2').permissive.get() == {'prop', 'prop2'}
cfg.permissive.importation(export) cfg.permissive.importation(export)
assert cfg.option('u2').permissive.get() == {'prop'} assert cfg.option('u2').permissive.get() == {'prop'}
@ -266,7 +265,7 @@ def test_cache_leadership():
#assert cache['ip_admin_eth0.netmask_admin_eth0'][None][0] == [None] #assert cache['ip_admin_eth0.netmask_admin_eth0'][None][0] == [None]
#assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None #assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None
cache = settings.get_cached() cache = settings.get_cached()
assert set(cache.keys()) == set(['ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0']) assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert set(cache['ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == {0} assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == {0}
@ -284,7 +283,7 @@ def test_cache_leadership():
#assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None #assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None
#assert cache['ip_admin_eth0.netmask_admin_eth0'][1][0] is None #assert cache['ip_admin_eth0.netmask_admin_eth0'][1][0] is None
cache = settings.get_cached() cache = settings.get_cached()
assert set(cache.keys()) == set(['ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0']) assert set(cache.keys()) == set([None, 'ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
assert set(cache['ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None]) assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([0, 1]) assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([0, 1])
@ -310,19 +309,19 @@ def test_cache_callback():
od1 = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5]) od1 = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.value.get() cfg.value.dict()
values = cfg._config_bag.context._impl_values_cache values = cfg._config_bag.context._impl_values_cache
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
compare(values.get_cached(), {'val1': {None: ('val', None)}, compare(values.get_cached(), {'val1': {None: ('val', None)},
'val2': {None: ('val', None)}, 'val2': {None: ('val', None)},
'val3': {None: ('yes', None)}, 'val3': {None: ('yes', None)},
'val4': {None: ('val', None)}, 'val4': {None: ('val', None)},
'val5': {None: (['yes'], None)}}) 'val5': {None: (['yes'], None)}})
cfg.option('val1').value.set('new') cfg.option('val1').value.set('new')
compare(values.get_cached(), {'val3': {None: ('yes', None)}, compare(values.get_cached(), {'val3': {None: ('yes', None)},
'val1': {None: ('new', None)}, 'val1': {None: ('new', None)},
'val5': {None: (['yes'], None)}}) 'val5': {None: (['yes'], None)}})
cfg.value.get() cfg.value.dict()
compare(values.get_cached(), {'val1': {None: ('new', None)}, compare(values.get_cached(), {'val1': {None: ('new', None)},
'val2': {None: ('new', None)}, 'val2': {None: ('new', None)},
'val3': {None: ('yes', None)}, 'val3': {None: ('yes', None)},
@ -335,7 +334,7 @@ def test_cache_callback():
'val1': {None: ('new', None)}, 'val1': {None: ('new', None)},
'val3': {None: ('new2', None, True)}, 'val3': {None: ('new2', None, True)},
'val5': {None: (['yes'], None)}}) 'val5': {None: (['yes'], None)}})
cfg.value.get() cfg.value.dict()
compare(values.get_cached(), {'val1': {None: ('new', None)}, compare(values.get_cached(), {'val1': {None: ('new', None)},
'val2': {None: ('new', None)}, 'val2': {None: ('new', None)},
'val3': {None: ('new2', None)}, 'val3': {None: ('new2', None)},
@ -347,7 +346,7 @@ def test_cache_callback():
'val3': {None: ('new2', None)}, 'val3': {None: ('new2', None)},
'val4': {None: ('new3', None, True)}, 'val4': {None: ('new3', None, True)},
'val5': {None: (['yes'], None)}}) 'val5': {None: (['yes'], None)}})
cfg.value.get() cfg.value.dict()
compare(values.get_cached(), {'val1': {None: ('new', None)}, compare(values.get_cached(), {'val1': {None: ('new', None)},
'val2': {None: ('new', None)}, 'val2': {None: ('new', None)},
'val3': {None: ('new2', None)}, 'val3': {None: ('new2', None)},
@ -363,11 +362,11 @@ def test_cache_leader_and_followers():
od1 = OptionDescription('rootconfig', '', [interface1]) od1 = OptionDescription('rootconfig', '', [interface1])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.value.get() cfg.value.dict()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value'] global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = [] val1_props = []
val1_val1_props = ['empty', 'unique', 'validator'] val1_val1_props = ['empty', 'unique']
val1_val2_props = ['validator'] val1_val2_props = []
global_props = frozenset(global_props) global_props = frozenset(global_props)
val1_props = frozenset(val1_props) val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_val1_props) val1_val1_props = frozenset(val1_val1_props)
@ -376,31 +375,35 @@ def test_cache_leader_and_followers():
idx_val2 = None idx_val2 = None
values = cfg._config_bag.context._impl_values_cache values = cfg._config_bag.context._impl_values_cache
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
compare(settings.get_cached(), {'val1': {None: (val1_props, None)}, compare(settings.get_cached(), {None: {None: (global_props, None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1': {None: (val1_props, None)},
}) 'val1.val1': {None: (val1_val1_props, None)},
})
# len is 0 so don't get any value # len is 0 so don't get any value
compare(values.get_cached(), {'val1.val1': {None: ([], None)}}) compare(values.get_cached(), {'val1.val1': {None: ([], None)}})
# #
cfg.option('val1.val1').value.set([None]) cfg.option('val1.val1').value.set([None])
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)} val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)}
compare(settings.get_cached(), {'val1.val1': {None: ({'validator', 'empty', 'unique'}, None, True)}}) compare(settings.get_cached(), {None: {None: (set(global_props), None)},
'val1.val1': {None: (val1_val1_props, None)},
})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get() cfg.value.dict()
#has value #has value
idx_val2 = 0 idx_val2 = 0
val_val2 = None val_val2 = None
val_val2_props = {idx_val2: (val1_val2_props, None)} val_val2_props = {idx_val2: (val1_val2_props, None)}
compare(settings.get_cached(), {'val1': {None: (val1_props, None)}, compare(settings.get_cached(), {None: {None: (global_props, None)},
'val1': {None: (val1_props, None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1.val1': {None: (val1_val1_props, None)},
'val1.val2': val_val2_props}) 'val1.val2': val_val2_props})
compare(values.get_cached(), {'val1.val1': {None: ([None], None)}, compare(values.get_cached(), {'val1.val1': {None: ([None], None)},
'val1.val2': {idx_val2: (val_val2, None)}, 'val1.val2': {idx_val2: (val_val2, None)},
}) })
cfg.option('val1.val1').value.set([None, None]) cfg.option('val1.val1').value.set([None, None])
cfg.value.get() cfg.value.dict()
cfg.option('val1.val2', 1).value.set('oui') cfg.option('val1.val2', 1).value.set('oui')
compare(settings.get_cached(), {}) compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)}})
compare(values.get_cached(), {'val1.val2': {1: ('oui', None, True)}}) compare(values.get_cached(), {'val1.val2': {1: ('oui', None, True)}})
val1_val2_props = {0: (frozenset([]), None), 1: (frozenset([]), None)} val1_val2_props = {0: (frozenset([]), None), 1: (frozenset([]), None)}
# assert not list_sessions() # assert not list_sessions()
@ -413,26 +416,29 @@ def test_cache_leader_callback():
od1 = OptionDescription('rootconfig', '', [interface1]) od1 = OptionDescription('rootconfig', '', [interface1])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.value.get() cfg.value.dict()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value'] global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = [] val1_props = []
val1_val1_props = ['empty', 'unique', 'validator'] val1_val1_props = ['empty', 'unique']
val1_val2_props = ['validator'] val1_val2_props = []
global_props = frozenset(global_props) global_props = frozenset(global_props)
val1_props = frozenset(val1_props) val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_val1_props) val1_val1_props = frozenset(val1_val1_props)
val1_val2_props = frozenset(val1_val2_props) val1_val2_props = frozenset(val1_val2_props)
values = cfg._config_bag.context._impl_values_cache values = cfg._config_bag.context._impl_values_cache
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
compare(settings.get_cached(), {'val1': {None: (val1_props, None)}, compare(settings.get_cached(), {None: {None: (global_props, None)},
'val1': {None: (val1_props, None)},
'val1.val1': {None: (val1_val1_props, None)}, 'val1.val1': {None: (val1_val1_props, None)},
}) })
compare(values.get_cached(), {'val1.val1': {None: ([], None)}}) compare(values.get_cached(), {'val1.val1': {None: ([], None)}})
cfg.option('val1.val1').value.set([None]) cfg.option('val1.val1').value.set([None])
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty', 'validator'}, None, True)}}) compare(settings.get_cached(), {None: {None: (set(global_props), None)},
'val1.val1': {None: (val1_val1_props, None)},
})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}}) compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -451,34 +457,39 @@ def test_cache_requires():
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {} assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({'validator'}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.value.get() cfg.value.dict()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.option('ip_address_service').value.set('1.1.1.1') cfg.option('ip_address_service').value.set('1.1.1.1')
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}}) compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'activate_service': {None: (set([]), None)}})
compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}}) compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}})
cfg.value.get() cfg.value.dict()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)}, compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.option('activate_service').value.set(False) cfg.option('activate_service').value.set(False)
compare(settings.get_cached(), {}) compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)}})
compare(values.get_cached(), {'activate_service': {None: (False, None)}}) compare(values.get_cached(), {'activate_service': {None: (False, None)}})
cfg.value.get() cfg.value.dict()
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({'disabled', "validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set(['disabled']), None)}})
compare(values.get_cached(), {'activate_service': {None: (False, None)}}) compare(values.get_cached(), {'activate_service': {None: (False, None)}})
# assert not list_sessions() # assert not list_sessions()
@ -499,19 +510,22 @@ def test_cache_global_properties():
settings = cfg._config_bag.context.properties_cache settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {} assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)}, compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}) 'activate_service': {None: (True, None)}})
cfg.property.remove('disabled') cfg.property.remove('disabled')
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
cfg.property.add('test') cfg.property.add('test')
assert cfg.option('ip_address_service').value.get() == None assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}, compare(settings.get_cached(), {None: {None: (set(['cache', 'frozen', 'hidden', 'validator', 'warnings', 'test', 'force_store_value']), None)},
'ip_address_service': {None: ({"validator"}, None)}}) 'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
# assert not list_sessions() # assert not list_sessions()

View file

@ -1,9 +1,10 @@
# coding: utf-8 # coding: utf-8
from py.test import raises
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import config_type, get_config, value_list, global_owner, parse_od_get from .config import config_type, get_config, value_list, global_owner
from pytest import raises
from tiramisu import ChoiceOption, StrOption, OptionDescription, Config, owners, Calculation, \ from tiramisu import ChoiceOption, StrOption, OptionDescription, Config, owners, Calculation, \
undefined, Params, ParamValue, ParamOption undefined, Params, ParamValue, ParamOption
from tiramisu.error import ConfigError from tiramisu.error import ConfigError
@ -72,30 +73,6 @@ def test_choiceoption_function(config_type):
assert cfg.option('choice').owner.isdefault() assert cfg.option('choice').owner.isdefault()
# #
assert value_list(cfg.option('choice').value.list()) == ('val1', 'val2') assert value_list(cfg.option('choice').value.list()) == ('val1', 'val2')
assert isinstance(cfg.option('choice').value.list(uncalculated=True), Calculation)
# assert not list_sessions()
def test_choiceoption_subfunction(config_type):
choice = ChoiceOption('choice', '', values=(Calculation(return_val, Params(ParamValue('val1'))), Calculation(return_val, Params(ParamValue('val2')))))
od1 = OptionDescription('od', '', [choice])
cfg = Config(od1)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
owner = global_owner(cfg, config_type)
assert cfg.option('choice').owner.isdefault()
#
cfg.option('choice').value.set('val1')
assert cfg.option('choice').owner.get() == owner
#
cfg.option('choice').value.reset()
assert cfg.option('choice').owner.isdefault()
#
with raises(ValueError):
cfg.option('choice').value.set('no')
assert cfg.option('choice').owner.isdefault()
#
assert value_list(cfg.option('choice').value.list()) == ('val1', 'val2')
# assert not list_sessions() # assert not list_sessions()
@ -162,10 +139,6 @@ def test_choiceoption_calc_opt_function(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('choice').owner.isdefault() assert cfg.option('choice').owner.isdefault()
# #
dep = cfg.option('str').dependencies()
assert len(dep) == 1
assert dep[0].path() == 'choice'
#
cfg.option('choice').value.set('val1') cfg.option('choice').value.set('val1')
assert cfg.option('choice').owner.get() == owner assert cfg.option('choice').owner.get() == owner
# #
@ -191,6 +164,7 @@ def test_choiceoption_calc_opt_function_propertyerror():
# assert not list_sessions() # assert not list_sessions()
#def test_choiceoption_calc_opt_multi_function(config_type):
def test_choiceoption_calc_opt_multi_function(): def test_choiceoption_calc_opt_multi_function():
# FIXME # FIXME
config_type = 'tiramisu' config_type = 'tiramisu'
@ -288,13 +262,3 @@ def test_choiceoption_calc_not_list():
with raises(ConfigError): with raises(ConfigError):
cfg.option('choice').value.set(['val1']) cfg.option('choice').value.set(['val1'])
# assert not list_sessions() # assert not list_sessions()
def test_choiceoption_calc_default_value():
var1 = StrOption("var1", '', default="val1")
var2 = StrOption("var2", '', default="val2")
choice = ChoiceOption("choice", '', values=(Calculation(return_val, Params((ParamOption(var1)))), Calculation(return_val, Params((ParamOption(var2))))), default="val1")
od2 = OptionDescription("rougail", '', children=[var1, var2, choice])
od1 = OptionDescription("baseoption", "", children=[od2])
cfg = Config(od1)
assert parse_od_get(cfg.value.get()) == {'rougail.var1': 'val1', 'rougail.var2': 'val2', 'rougail.choice': 'val1'}

View file

@ -9,14 +9,10 @@ do_autopath()
from .config import config_type, get_config, value_list, global_owner from .config import config_type, get_config, value_list, global_owner
import pytest import pytest
from tiramisu import Config, Calculation, Params, ParamSelfInformation, calc_value from tiramisu import Config
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu import Config, IntOption, FloatOption, ChoiceOption, \ from tiramisu import Config, IntOption, FloatOption, ChoiceOption, \
BoolOption, StrOption, SymLinkOption, OptionDescription, undefined, \ BoolOption, StrOption, SymLinkOption, OptionDescription, undefined
DomainnameOption, EmailOption, URLOption, RegexpOption, IPOption, \
PortOption, NetworkOption, NetmaskOption, BroadcastOption, UsernameOption, \
GroupnameOption, DateOption, FilenameOption, PasswordOption, MACOption, \
PermissionsOption
from tiramisu.error import ConflictError, ConfigError, PropertiesOptionError from tiramisu.error import ConflictError, ConfigError, PropertiesOptionError
@ -30,7 +26,8 @@ def make_description():
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc", properties=('mandatory', )) stroption = StrOption('str', 'Test string option', default="abc", properties=('mandatory', ))
boolop = BoolOption('boolop', 'Test boolean option op', default=True, properties=('hidden',)) boolop = BoolOption('boolop', 'Test boolean option op', default=True, properties=('hidden',))
wantref_option = BoolOption('wantref', 'Test requires', default=False, informations={'info': 'default value'}) wantref_option = BoolOption('wantref', 'Test requires', default=False)
wantref_option.impl_set_information('info', 'default value')
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False) default=False)
@ -133,29 +130,6 @@ def test_not_valid_properties():
# assert not list_sessions() # assert not list_sessions()
def test_information_load():
ChoiceOption('a', '', ('a', 'b'), informations={'info': 'value'})
BoolOption('a', '', informations={'info': 'value'})
IntOption('a', '', informations={'info': 'value'})
FloatOption('a', '', informations={'info': 'value'})
StrOption('a', '', informations={'info': 'value'})
RegexpOption('a', '', informations={'info': 'value'})
IPOption('a', '', informations={'info': 'value'})
PortOption('a', '', informations={'info': 'value'})
NetworkOption('a', '', informations={'info': 'value'})
NetmaskOption('a', '', informations={'info': 'value'})
BroadcastOption('a', '', informations={'info': 'value'})
DomainnameOption('a', '', informations={'info': 'value'})
EmailOption('a', '', informations={'info': 'value'})
URLOption('a', '', informations={'info': 'value'})
UsernameOption('a', '', informations={'info': 'value'})
GroupnameOption('a', '', informations={'info': 'value'})
DateOption('a', '', informations={'info': 'value'})
FilenameOption('a', '', informations={'info': 'value'})
PasswordOption('a', '', informations={'info': 'value'})
MACOption('a', '', informations={'info': 'value'})
PermissionsOption('a', '', informations={'info': 'value'})
def test_information_config(): def test_information_config():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
@ -169,11 +143,11 @@ def test_information_config():
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.information.get('noinfo') cfg.information.get('noinfo')
assert cfg.information.get('noinfo', 'default') == 'default' assert cfg.information.get('noinfo', 'default') == 'default'
cfg.information.remove('info') cfg.information.reset('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.information.remove('info') cfg.information.get('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.information.remove('noinfo') cfg.information.reset('noinfo')
assert list(cfg.information.list()) == ['doc'] assert list(cfg.information.list()) == ['doc']
# assert not list_sessions() # assert not list_sessions()
@ -220,23 +194,26 @@ def test_information_option():
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc.name').information.get('noinfo') cfg.option('gc.name').information.get('noinfo')
assert cfg.option('gc.name').information.get('noinfo', 'default') == 'default' assert cfg.option('gc.name').information.get('noinfo', 'default') == 'default'
cfg.option('gc.name').information.remove('info') cfg.option('gc.name').information.reset('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc.name').information.get('info') cfg.option('gc.name').information.get('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc.name').information.remove('noinfo') cfg.option('gc.name').information.reset('noinfo')
assert list(cfg.option('gc.name').information.list()) == ['doc'] assert list(cfg.option('gc.name').information.list()) == ['doc']
# #
assert cfg.option('wantref').information.get('info') == 'default value' assert cfg.option('wantref').information.get('info') == 'default value'
cfg.option('wantref').information.set('info', 'default value') cfg.option('wantref').information.set('info', 'default value')
assert cfg.option('wantref').information.get('info') == 'default value' assert cfg.option('wantref').information.get('info') == 'default value'
cfg.option('wantref').information.remove('info') cfg.option('wantref').information.reset('info')
assert cfg.option('wantref').information.get('info') == 'default value' assert cfg.option('wantref').information.get('info') == 'default value'
# assert not list_sessions() # assert not list_sessions()
def test_information_option_2(): def test_information_option_2():
i1 = IntOption('test1', '', informations={'info': 'value'}) i1 = IntOption('test1', '')
i1.impl_set_information('info', 'value')
# it's a dict
assert set(i1.impl_list_information()) == {'info', 'doc'}
od1 = OptionDescription('test', '', [i1]) od1 = OptionDescription('test', '', [i1])
cfg = Config(od1) cfg = Config(od1)
# it's tuples # it's tuples
@ -244,17 +221,6 @@ def test_information_option_2():
# assert not list_sessions() # assert not list_sessions()
def test_information_option_symlink():
i1 = IntOption('test1', '', Calculation(calc_value, Params(ParamSelfInformation('info'))), informations={'info': 'value'})
i2 = SymLinkOption('test2', i1)
od1 = OptionDescription('test', '', [i2, i1])
cfg = Config(od1)
# it's tuples
assert set(cfg.option('test1').information.list()) == {'info', 'doc'}
assert set(cfg.option('test2').information.list()) == {'info', 'doc'}
# assert not list_sessions()
def test_information_optiondescription(): def test_information_optiondescription():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
@ -268,11 +234,11 @@ def test_information_optiondescription():
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc').information.get('noinfo') cfg.option('gc').information.get('noinfo')
assert cfg.option('gc').information.get('noinfo', 'default') == 'default' assert cfg.option('gc').information.get('noinfo', 'default') == 'default'
cfg.option('gc').information.remove('info') cfg.option('gc').information.reset('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc').information.get('info') cfg.option('gc').information.get('info')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('gc').information.remove('noinfo') cfg.option('gc').information.reset('noinfo')
assert list(cfg.option('gc').information.list()) == ['doc'] assert list(cfg.option('gc').information.list()) == ['doc']
# assert not list_sessions() # assert not list_sessions()
@ -332,7 +298,7 @@ def test_duplicated_option():
g1 g1
#in same OptionDescription #in same OptionDescription
with pytest.raises(ConflictError): with pytest.raises(ConflictError):
OptionDescription('od', '', [g1, g1]) d1 = OptionDescription('od', '', [g1, g1])
# assert not list_sessions() # assert not list_sessions()
@ -346,28 +312,6 @@ def test_duplicated_option_diff_od():
Config(d2) Config(d2)
def test_duplicated_option_diff_od_2():
g1 = IntOption('g1', '', 1)
d1 = OptionDescription('od1', '', [g1])
#in different OptionDescription
d2 = OptionDescription('od2', '', [d1, g1])
d2
with pytest.raises(ConflictError):
Config(d2)
def test_duplicated_option_diff_od_3():
g1 = IntOption('g1', '', 1)
d1 = OptionDescription('od1', '', [g1])
d3 = OptionDescription('od3', '', [g1])
#in different OptionDescription
d2 = OptionDescription('od2', '', [d1, d3])
d4 = OptionDescription('od4', '', [d2])
d4
with pytest.raises(ConflictError):
Config(d4)
def test_cannot_assign_value_to_option_description(): def test_cannot_assign_value_to_option_description():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
@ -402,12 +346,12 @@ def test_prefix_error():
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}", it\'s not an integer').format('yes', _('integer'), 'test1') assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('yes', _('integer'), 'test1')
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('it\'s not an integer') assert str(err) == _('invalid value')
# assert not list_sessions() # assert not list_sessions()
@ -468,7 +412,7 @@ def test_config_od_name(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('val.i').name() == 'i' assert cfg.option('val.i').name() == 'i'
assert cfg.option('val.s').name() == 's' assert cfg.option('val.s').name() == 's'
assert cfg.option('val.s').type() == 'integer' assert cfg.option('val.s').type() == _('integer')
assert cfg.option('val').type() == 'optiondescription' assert cfg.option('val').type() == 'optiondescription'
# assert not list_sessions() # assert not list_sessions()
@ -480,7 +424,7 @@ def test_config_od_type(config_type):
cfg = Config(o2) cfg = Config(o2)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('val').type() == 'optiondescription' assert cfg.option('val').type() == 'optiondescription'
assert cfg.option('val.i').type() == 'integer' assert cfg.option('val.i').type() == _('integer')
# assert not list_sessions() # assert not list_sessions()

View file

@ -3,7 +3,7 @@ from pytest import raises
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import config_type, get_config, value_list, global_owner, parse_od_get from .config import config_type, get_config, value_list, global_owner
from tiramisu import Config, IntOption, FloatOption, StrOption, ChoiceOption, \ from tiramisu import Config, IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, FilenameOption, SymLinkOption, IPOption, \ BoolOption, FilenameOption, SymLinkOption, IPOption, \
@ -13,6 +13,12 @@ from tiramisu.error import PropertiesOptionError, ValueWarning, ConfigError
import warnings import warnings
#def teardown_function(function):
# # test_od_not_list emit a warnings because of doesn't create a Config
# with warnings.catch_warnings(record=True) as w:
# assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__)
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -70,12 +76,14 @@ def test_make_dict(config_type):
cfg.property.read_write() cfg.property.read_write()
cfg.permissive.add('hidden') cfg.permissive.add('hidden')
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {"s1.a": False, "int": 42} d = cfg.value.dict()
assert d == {"s1.a": False, "int": 42}
cfg.option('int').value.set(43) cfg.option('int').value.set(43)
cfg.option('s1.a').value.set(True) cfg.option('s1.a').value.set(True)
assert parse_od_get(cfg.value.get()) == {"s1.a": True, "int": 43} d = cfg.value.dict()
assert d == {"s1.a": True, "int": 43}
if config_type == 'tiramisu': if config_type == 'tiramisu':
assert parse_od_get(cfg.forcepermissive.value.get()) == {"s1.a": True, "s1.b": False, "int": 43} assert cfg.forcepermissive.value.dict() == {"s1.a": True, "s1.b": False, "int": 43}
# assert not list_sessions() # assert not list_sessions()
@ -90,7 +98,22 @@ def test_make_dict_sub(config_type):
cfg.property.read_write() cfg.property.read_write()
cfg.permissive.add('hidden') cfg.permissive.add('hidden')
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.option('s1').value.get()) == {'s1.a': False} assert cfg.option('s1').value.dict() == {'s1.a': False}
def test_make_dict_not_value(config_type):
"serialization part of config to a dict"
od1 = OptionDescription("opt", "", [
OptionDescription("s1", "", [
BoolOption("a", "", default=False),
BoolOption("b", "", default=False, properties=('hidden',))]),
IntOption("int", "", default=42)])
cfg = Config(od1)
cfg.property.read_write()
cfg.permissive.add('hidden')
cfg = get_config(cfg, config_type)
with raises(ConfigError):
cfg.option('s1.a').value.dict()
def test_make_dict_with_disabled(config_type): def test_make_dict_with_disabled(config_type):
@ -105,10 +128,10 @@ def test_make_dict_with_disabled(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {"s1.a": False, "int": 42} assert cfg.value.dict() == {"s1.a": False, "int": 42}
if config_type == 'tiramisu': if config_type == 'tiramisu':
assert parse_od_get(cfg.forcepermissive.value.get()) == {"s1.a": False, "int": 42} assert cfg.forcepermissive.value.dict() == {"s1.a": False, "int": 42}
assert parse_od_get(cfg.unrestraint.value.get()) == {"int": 42, "s1.a": False, "s1.b": False, "s2.a": False, "s2.b": False} assert cfg.unrestraint.value.dict() == {"int": 42, "s1.a": False, "s1.b": False, "s2.a": False, "s2.b": False}
# assert not list_sessions() # assert not list_sessions()
@ -124,7 +147,8 @@ def test_make_dict_with_disabled_in_callback(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {"s1.a": False, "int": 42} d = cfg.value.dict()
assert d == {"s1.a": False, "int": 42}
# assert not list_sessions() # assert not list_sessions()
@ -142,150 +166,150 @@ def test_make_dict_fullpath(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {"opt.s1.a": False, "opt.int": 42, "introot": 42} assert cfg.value.dict() == {"opt.s1.a": False, "opt.int": 42, "introot": 42}
assert parse_od_get(cfg.option('opt').value.get()) == {"opt.s1.a": False, "opt.int": 42} assert cfg.option('opt').value.dict() == {"opt.s1.a": False, "opt.int": 42}
# assert not list_sessions() # assert not list_sessions()
#def test_find_in_config(): def test_find_in_config():
# "finds option in config" "finds option in config"
# od1 = make_description() od1 = make_description()
# cfg = Config(od1) cfg = Config(od1)
# cfg.property.read_only() cfg.property.read_only()
# cfg.permissive.add('hidden') cfg.permissive.add('hidden')
# ret = list(cfg.option.find('dummy')) ret = list(cfg.option.find('dummy'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get()) _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get())
# # #
# ret_find = cfg.option.find('dummy', first=True) ret_find = cfg.option.find('dummy', first=True)
# ret = ret_find.get() ret = ret_find.get()
# _is_same_opt(ret, cfg.option('gc.dummy').get()) _is_same_opt(ret, cfg.option('gc.dummy').get())
# # #
# ret = list(cfg.option.find('float')) ret = list(cfg.option.find('float'))
# assert len(ret) == 2 assert len(ret) == 2
# _is_same_opt(ret[0].get(), cfg.option('gc.float').get()) _is_same_opt(ret[0].get(), cfg.option('gc.float').get())
# _is_same_opt(ret[1].get(), cfg.option('float').get()) _is_same_opt(ret[1].get(), cfg.option('float').get())
# # #
# ret = cfg.option.find('bool', first=True) ret = cfg.option.find('bool', first=True)
# _is_same_opt(ret.get(), cfg.option('gc.gc2.bool').get()) _is_same_opt(ret.get(), cfg.option('gc.gc2.bool').get())
# ret = cfg.option.find('bool', value=True, first=True) ret = cfg.option.find('bool', value=True, first=True)
# _is_same_opt(ret.get(), cfg.option('bool').get()) _is_same_opt(ret.get(), cfg.option('bool').get())
# ret = cfg.option.find('dummy', first=True) ret = cfg.option.find('dummy', first=True)
# _is_same_opt(ret.get(), cfg.option('gc.dummy').get()) _is_same_opt(ret.get(), cfg.option('gc.dummy').get())
# ret = cfg.option.find('float', first=True) ret = cfg.option.find('float', first=True)
# _is_same_opt(ret.get(), cfg.option('gc.float').get()) _is_same_opt(ret.get(), cfg.option('gc.float').get())
# ret = list(cfg.option.find('prop')) ret = list(cfg.option.find('prop'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.prop').get()) _is_same_opt(ret[0].get(), cfg.option('gc.prop').get())
# # #
# ret = list(cfg.option.find('prop', value=None)) ret = list(cfg.option.find('prop', value=None))
# assert len(ret) == 1 assert len(ret) == 1
# ret = list(cfg.option.find('prop')) ret = list(cfg.option.find('prop'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.prop').get()) _is_same_opt(ret[0].get(), cfg.option('gc.prop').get())
# # #
# cfg.property.read_write() cfg.property.read_write()
# with raises(AttributeError): with raises(AttributeError):
# ret = cfg.option.find('prop') ret = cfg.option.find('prop')
# assert ret.get() assert ret.get()
# ret = list(cfg.unrestraint.option.find(name='prop')) ret = list(cfg.unrestraint.option.find(name='prop'))
# assert len(ret) == 2 assert len(ret) == 2
# _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get()) _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get())
# _is_same_opt(ret[1].get(), cfg.forcepermissive.option('gc.prop').get()) _is_same_opt(ret[1].get(), cfg.forcepermissive.option('gc.prop').get())
# # #
# ret = list(cfg.forcepermissive.option.find('prop')) ret = list(cfg.forcepermissive.option.find('prop'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.forcepermissive.option('gc.prop').get()) _is_same_opt(ret[0].get(), cfg.forcepermissive.option('gc.prop').get())
# # #
# ret = cfg.forcepermissive.option.find('prop', first=True) ret = cfg.forcepermissive.option.find('prop', first=True)
# _is_same_opt(ret.get(), cfg.forcepermissive.option('gc.prop').get()) _is_same_opt(ret.get(), cfg.forcepermissive.option('gc.prop').get())
# # combinaison of filters # combinaison of filters
# ret = list(cfg.unrestraint.option.find('prop', type=BoolOption)) ret = list(cfg.unrestraint.option.find('prop', type=BoolOption))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get()) _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get())
# ret = cfg.unrestraint.option.find('prop', type=BoolOption, first=True) ret = cfg.unrestraint.option.find('prop', type=BoolOption, first=True)
# _is_same_opt(ret.get(), cfg.unrestraint.option('gc.gc2.prop').get()) _is_same_opt(ret.get(), cfg.unrestraint.option('gc.gc2.prop').get())
# # #
# ret = list(cfg.option.find('dummy', value=False)) ret = list(cfg.option.find('dummy', value=False))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get()) _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get())
# # #
# ret = cfg.option.find('dummy', value=False, first=True) ret = cfg.option.find('dummy', value=False, first=True)
# _is_same_opt(ret.get(), cfg.option('gc.dummy').get()) _is_same_opt(ret.get(), cfg.option('gc.dummy').get())
# #subcfgig #subcfgig
# ret = list(cfg.option('gc').find('dummy')) ret = list(cfg.option('gc').find('dummy'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get()) _is_same_opt(ret[0].get(), cfg.option('gc.dummy').get())
# # #
# ret = list(cfg.option('gc').find('float')) ret = list(cfg.option('gc').find('float'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.float').get()) _is_same_opt(ret[0].get(), cfg.option('gc.float').get())
# # #
# ret = list(cfg.option('gc.gc2').find('bool')) ret = list(cfg.option('gc.gc2').find('bool'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.gc2.bool').get()) _is_same_opt(ret[0].get(), cfg.option('gc.gc2.bool').get())
# ret = cfg.option('gc').find('bool', value=False, first=True) ret = cfg.option('gc').find('bool', value=False, first=True)
# _is_same_opt(ret.get(), cfg.option('gc.gc2.bool').get()) _is_same_opt(ret.get(), cfg.option('gc.gc2.bool').get())
# # #
# with raises(AttributeError): with raises(AttributeError):
# ret = cfg.option('gc').find('bool', value=True, first=True) ret = cfg.option('gc').find('bool', value=True, first=True)
# assert ret.get() assert ret.get()
# # #
# with raises(AttributeError): with raises(AttributeError):
# ret = cfg.option('gc').find('wantref') ret = cfg.option('gc').find('wantref')
# ret.get() ret.get()
# # #
# ret = list(cfg.unrestraint.option('gc').find('prop')) ret = list(cfg.unrestraint.option('gc').find('prop'))
# assert len(ret) == 2 assert len(ret) == 2
# _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get()) _is_same_opt(ret[0].get(), cfg.unrestraint.option('gc.gc2.prop').get())
# _is_same_opt(ret[1].get(), cfg.forcepermissive.option('gc.prop').get()) _is_same_opt(ret[1].get(), cfg.forcepermissive.option('gc.prop').get())
# # #
# cfg.property.read_only() cfg.property.read_only()
# ret = list(cfg.option('gc').find('prop')) ret = list(cfg.option('gc').find('prop'))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), cfg.option('gc.prop').get()) _is_same_opt(ret[0].get(), cfg.option('gc.prop').get())
# # not OptionDescription # not OptionDescription
# with raises(AttributeError): with raises(AttributeError):
# cfg.option.find('gc', first=True) cfg.option.find('gc', first=True)
# with raises(AttributeError): with raises(AttributeError):
# cfg.option.find('gc2', first=True) cfg.option.find('gc2', first=True)
## assert not list_sessions() # assert not list_sessions()
#
#
#def test_find_multi(): def test_find_multi():
# b = BoolOption('bool', '', multi=True, properties=('notunique',)) b = BoolOption('bool', '', multi=True, properties=('notunique',))
# od1 = OptionDescription('od', '', [b]) od1 = OptionDescription('od', '', [b])
# cfg = Config(od1) cfg = Config(od1)
# # #
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True)) list(cfg.option.find('bool', value=True))
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True, first=True)) list(cfg.option.find('bool', value=True, first=True))
# cfg.option('bool').value.set([False]) cfg.option('bool').value.set([False])
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True)) list(cfg.option.find('bool', value=True))
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True, first=True)) list(cfg.option.find('bool', value=True, first=True))
# cfg.option('bool').value.set([False, False]) cfg.option('bool').value.set([False, False])
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True)) list(cfg.option.find('bool', value=True))
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('bool', value=True, first=True)) list(cfg.option.find('bool', value=True, first=True))
# cfg.option('bool').value.set([False, False, True]) cfg.option('bool').value.set([False, False, True])
# ret = list(cfg.option.find('bool', value=True)) ret = list(cfg.option.find('bool', value=True))
# assert len(ret) == 1 assert len(ret) == 1
# _is_same_opt(ret[0].get(), b) _is_same_opt(ret[0].get(), b)
# ret = cfg.option.find('bool', value=True, first=True) ret = cfg.option.find('bool', value=True, first=True)
# _is_same_opt(ret.get(), b) _is_same_opt(ret.get(), b)
## assert not list_sessions() # assert not list_sessions()
#
#
#def test_does_not_find_in_config(): def test_does_not_find_in_config():
# od1 = make_description() od1 = make_description()
# cfg = Config(od1) cfg = Config(od1)
# with raises(AttributeError): with raises(AttributeError):
# list(cfg.option.find('IDontExist')) list(cfg.option.find('IDontExist'))
## assert not list_sessions() # assert not list_sessions()
def test_filename(config_type): def test_filename(config_type):
@ -346,8 +370,6 @@ def test_invalid_option():
PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False) PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False)
with raises(ValueError): with raises(ValueError):
PortOption('a', '', 'tcp:80') PortOption('a', '', 'tcp:80')
with raises(ValueError):
PortOption('a', '', '')
NetworkOption('a', '') NetworkOption('a', '')
with raises(ValueError): with raises(ValueError):
NetworkOption('a', '', 'string') NetworkOption('a', '', 'string')
@ -408,6 +430,7 @@ def test_help():
cfg = Config(od2) cfg = Config(od2)
cfg.help(_display=False) cfg.help(_display=False)
cfg.config.help(_display=False) cfg.config.help(_display=False)
cfg.option.help(_display=False)
cfg.option('o').help(_display=False) cfg.option('o').help(_display=False)
cfg.option('o.s').help(_display=False) cfg.option('o.s').help(_display=False)
# assert not list_sessions() # assert not list_sessions()
@ -419,13 +442,13 @@ def test_config_reset():
cfg.owner.set('test') cfg.owner.set('test')
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() assert not cfg.option('gc.gc2.bool').value.get()
assert cfg.option('boolop').property.get() == frozenset(["validator"]) assert not cfg.option('boolop').property.get()
assert not cfg.option('boolop').permissive.get() assert not cfg.option('boolop').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# #
cfg.option('gc.gc2.bool').value.set(True) cfg.option('gc.gc2.bool').value.set(True)
cfg.option('boolop').property.add('test') cfg.option('boolop').property.add('test')
cfg.option('float').permissive.add('test') cfg.option('float').permissive.set(frozenset(['test']))
cfg.option('wantref').information.set('info', 'info') cfg.option('wantref').information.set('info', 'info')
assert cfg.option('gc.gc2.bool').value.get() assert cfg.option('gc.gc2.bool').value.get()
assert cfg.option('boolop').property.get() assert cfg.option('boolop').property.get()
@ -436,7 +459,7 @@ def test_config_reset():
cfg.config.reset() cfg.config.reset()
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() assert not cfg.option('gc.gc2.bool').value.get()
assert cfg.option('boolop').property.get() == {"validator"} assert not cfg.option('boolop').property.get()
assert not cfg.option('float').permissive.get() assert not cfg.option('float').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# assert not list_sessions() # assert not list_sessions()

View file

@ -275,9 +275,3 @@ def test_url(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('u').value.set('https://FOO.COM:8443') cfg.option('u').value.set('https://FOO.COM:8443')
# assert not list_sessions() # assert not list_sessions()
def test_domainname_existence():
DomainnameOption('d', '', 'google.fr', test_existence=True)
with pytest.raises(ValueError):
DomainnameOption('d', '', 'ljijouuuehyfr.com', test_existence=True)

View file

@ -21,7 +21,6 @@ def test_ip(config_type):
cfg.option('a').value.set('192.168.1.0') cfg.option('a').value.set('192.168.1.0')
cfg.option('a').value.set('88.88.88.88') cfg.option('a').value.set('88.88.88.88')
cfg.option('a').value.set('0.0.0.0') cfg.option('a').value.set('0.0.0.0')
cfg.option('a').value.set('2001:db8::1')
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME # FIXME
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -149,7 +148,8 @@ def test_network_cidr(config_type):
cfg.option('a').value.set('192.168.1.1') cfg.option('a').value.set('192.168.1.1')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('a').value.set('192.168.1.1/24') cfg.option('a').value.set('192.168.1.1/24')
cfg.option('a').value.set('2001:db00::0/24') with pytest.raises(ValueError):
cfg.option('a').value.set('2001:db00::0/24')
# assert not list_sessions() # assert not list_sessions()
@ -197,7 +197,8 @@ def test_broadcast(config_type):
cfg.option('a').value.set(1) cfg.option('a').value.set(1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('a').value.set(2) cfg.option('a').value.set(2)
cfg.option('a').value.set('2001:db8::1') with pytest.raises(ValueError):
cfg.option('a').value.set('2001:db8::1')
cfg.option('a').value.set('0.0.0.0') cfg.option('a').value.set('0.0.0.0')
cfg.option('a').value.set('255.255.255.0') cfg.option('a').value.set('255.255.255.0')
# assert not list_sessions() # assert not list_sessions()

View file

@ -201,7 +201,7 @@ def test_deref_symlink():
def test_deref_dyn(): def test_deref_dyn():
a = StrOption('a', '', ['val1', 'val2'], multi=True) a = StrOption('a', '', ['val1', 'val2'], multi=True)
b = StrOption('b', '') b = StrOption('b', '')
dod = DynOptionDescription('dod', '', [b], identifiers=Calculation(funcname, Params((ParamOption(a),)))) dod = DynOptionDescription('dod', '', [b], suffixes=Calculation(funcname, Params((ParamOption(a),))))
o = OptionDescription('od', '', [dod, a]) o = OptionDescription('od', '', [dod, a])
cfg = Config(o) cfg = Config(o)
w = weakref.ref(a) w = weakref.ref(a)

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
#test_force_store_value coding: utf-8 # coding: utf-8
"frozen and hidden values" "frozen and hidden values"
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
@ -165,15 +165,15 @@ def test_force_store_value():
cfg = Config(od1) cfg = Config(od1)
compare(cfg.value.exportation(), {}) compare(cfg.value.exportation(), {})
cfg.property.read_write() cfg.property.read_write()
compare(cfg.value.exportation(), {'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}})
cfg.option('bool').value.set(False) cfg.option('bool').value.set(False)
cfg.option('wantref').value.set(True) cfg.option('wantref').value.set(True)
cfg.option('bool').value.reset() cfg.option('bool').value.reset()
compare(cfg.value.exportation(), {'wantref': {None: [True, 'user']}, 'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref': {None: [True, 'user']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}})
cfg.option('bool').value.set(False) cfg.option('bool').value.set(False)
cfg.option('wantref').value.reset() cfg.option('wantref').value.reset()
cfg.option('bool').value.reset() cfg.option('bool').value.reset()
compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}})
# assert not list_sessions() # assert not list_sessions()

View file

@ -1,12 +1,12 @@
# coding: utf-8 # coding: utf-8
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import config_type, get_config, value_list, global_owner, parse_od_get from .config import config_type, get_config, value_list, global_owner
import pytest import pytest
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \ from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, ParamOption, calc_value, Params, submulti StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, calc_value, Params
from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError
@ -59,10 +59,10 @@ def test_base_config(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('creole.general.activer_proxy_client').value.get() is False assert cfg.option('creole.general.activer_proxy_client').value.get() is False
assert cfg.option('creole.general.nom_machine').value.get() == "eoleng" assert cfg.option('creole.general.nom_machine').value.get() == "eoleng"
# if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# ret = cfg.option.find('nom_machine', first=True) ret = cfg.option.find('nom_machine', first=True)
# assert ret.value.get() == "eoleng" assert ret.value.get() == "eoleng"
assert parse_od_get(cfg.option('creole').value.get()) == {'creole.general.numero_etab': None, 'creole.general.nom_machine': 'eoleng', 'creole.general.nombre_interfaces': 1, 'creole.general.activer_proxy_client': False, 'creole.general.mode_conteneur_actif': False, 'creole.general.serveur_ntp': [], 'creole.general.time_zone': 'Paris', 'creole.interface1.ip_admin_eth0.ip_admin_eth0': None, 'creole.interface1.ip_admin_eth0.netmask_admin_eth0': None} assert cfg.option('creole').value.dict() == {'creole.general.numero_etab': None, 'creole.general.nom_machine': 'eoleng', 'creole.general.nombre_interfaces': 1, 'creole.general.activer_proxy_client': False, 'creole.general.mode_conteneur_actif': False, 'creole.general.serveur_ntp': [], 'creole.general.time_zone': 'Paris', 'creole.interface1.ip_admin_eth0.ip_admin_eth0': None, 'creole.interface1.ip_admin_eth0.netmask_admin_eth0': None}
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
# assert not list_sessions() # assert not list_sessions()
@ -77,40 +77,49 @@ def test_get_group_type():
assert grp.group_type() == 'family' assert grp.group_type() == 'family'
assert isinstance(grp.group_type(), groups.GroupType) assert isinstance(grp.group_type(), groups.GroupType)
# assert not list_sessions() # assert not list_sessions()
#
#
#def test_iter_on_groups(): def test_iter_on_groups():
# od1 = make_description() od1 = make_description()
# cfg = Config(od1) cfg = Config(od1)
# cfg.property.read_write() cfg.property.read_write()
# result = cfg.option('creole').list('optiondescription', result = cfg.option('creole').list('optiondescription',
# group_type=groups.family, group_type=groups.family,
# ) )
# group_names = [res.name() for res in result] group_names = [res.name() for res in result]
# assert group_names == ['general', 'interface1'] assert group_names == ['general', 'interface1']
# for i in cfg.option('creole').list('optiondescription', for i in cfg.option('creole').list('optiondescription',
# group_type=groups.family, group_type=groups.family,
# ): ):
# #test StopIteration #test StopIteration
# break break
# result = cfg.option('creole').list('option', result = cfg.option('creole').list('option',
# group_type=groups.family, group_type=groups.family,
# ) )
# assert list(result) == [] assert list(result) == []
# result = cfg.option('creole.general').list('optiondescription', result = cfg.option('creole.general').list('optiondescription',
# group_type=groups.family, group_type=groups.family,
# ) )
# assert list(result) == [] assert list(result) == []
## assert not list_sessions() # assert not list_sessions()
def test_list_recursive(): def test_list_recursive():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
result = cfg.option('creole').list() result = cfg.option('creole').list('all')
group_names = [res.name() for res in result] group_names = [res.name() for res in result]
assert group_names == ['general', 'interface1'] assert group_names == ['general', 'interface1']
# #
result = cfg.option.list(recursive=True)
group_names = [res.name() for res in result]
assert group_names == ['numero_etab', 'nom_machine', 'nombre_interfaces',
'activer_proxy_client', 'mode_conteneur_actif',
'serveur_ntp', 'time_zone', 'ip_admin_eth0',
'netmask_admin_eth0']
result = list(cfg.option.list(recursive=True, type='optiondescription'))
group_names = [res.name() for res in result]
assert group_names == ['creole', 'general', 'interface1', 'ip_admin_eth0']
# assert not list_sessions() # assert not list_sessions()
@ -138,7 +147,8 @@ def test_iter_group_on_groups_force_permissive():
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.permissive.add('hidden') cfg.permissive.add('hidden')
result = cfg.forcepermissive.option('creole').list() result = cfg.forcepermissive.option('creole').list(type='optiondescription',
group_type=groups.family)
group_names = [res.name() for res in result] group_names = [res.name() for res in result]
assert group_names == ['general', 'interface1', 'new'] assert group_names == ['general', 'interface1', 'new']
# assert not list_sessions() # assert not list_sessions()
@ -149,7 +159,8 @@ def test_iter_on_groups_props():
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.option('creole.interface1').property.add('disabled') cfg.option('creole.interface1').property.add('disabled')
result = cfg.option('creole').list() result = cfg.option('creole').list(type='optiondescription',
group_type=groups.family)
group_names = [res.name() for res in result] group_names = [res.name() for res in result]
assert group_names == ['general'] assert group_names == ['general']
# assert not list_sessions() # assert not list_sessions()
@ -159,11 +170,20 @@ def test_iter_on_empty_group():
od1 = OptionDescription("name", "descr", []) od1 = OptionDescription("name", "descr", [])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
result = list(cfg.list()) result = list(cfg.option.list(type='optiondescription'))
assert result == [] assert result == []
# assert not list_sessions() # assert not list_sessions()
def test_iter_not_group():
od1 = OptionDescription("name", "descr", [])
cfg = Config(od1)
cfg.property.read_write()
with pytest.raises(AssertionError):
print(list(cfg.option.list(type='optiondescription', group_type='family')))
# assert not list_sessions()
def test_groups_with_leader(): def test_groups_with_leader():
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)
@ -194,51 +214,32 @@ def test_groups_is_leader(config_type):
assert not cfg.option('leadership.netmask_admin_eth0').isleader() assert not cfg.option('leadership.netmask_admin_eth0').isleader()
assert cfg.option('leadership.netmask_admin_eth0').isfollower() assert cfg.option('leadership.netmask_admin_eth0').isfollower()
assert cfg.option('leadership.netmask_admin_eth0').path() == 'leadership.netmask_admin_eth0' assert cfg.option('leadership.netmask_admin_eth0').path() == 'leadership.netmask_admin_eth0'
assert cfg.option('leadership.netmask_admin_eth0').value.defaultmulti() == 'value' assert cfg.option('leadership.netmask_admin_eth0').defaultmulti() == 'value'
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
# assert not list_sessions() # assert not list_sessions()
def test_leader_follower_index():
ip = StrOption('ip', "", ['val1', 'val2'], multi=True)
netmask = StrOption('netmask', "", multi=True, default_multi='value')
interface1 = Leadership('leadership', '', [ip, netmask])
od1 = OptionDescription('od', '', [interface1])
cfg = Config(od1)
assert cfg.option('leadership.ip').value.get() == ['val1', 'val2']
with pytest.raises(ConfigError):
cfg.option('leadership.ip').index(0)
follower = cfg.option('leadership.netmask')
with pytest.raises(ConfigError):
follower.value.get()
assert follower.index(0).value.get() == 'value'
assert follower.index(0).index(None).index() is None
with pytest.raises(ConfigError):
follower.index(0).index(None).value.get()
def test_leader_list(config_type): def test_leader_list(config_type):
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", ['val1'], 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, default_multi='value') netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, default_multi='value')
interface1 = Leadership('leadership', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('leadership', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('od', '', [interface1]) od1 = OptionDescription('od', '', [interface1])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
ret = cfg.list() ret = cfg.option.list('all')
assert len(ret) == 1 assert len(ret) == 1
assert ret[0].name() == 'leadership' assert ret[0].name() == 'leadership'
# #
ret = cfg.option('leadership').list() ret = cfg.option('leadership').list('all')
assert len(ret) == 2 assert len(ret) == 2
assert ret[0].name() == 'ip_admin_eth0' assert ret[0].name() == 'ip_admin_eth0'
assert ret[1].name() == 'netmask_admin_eth0' assert ret[1].name() == 'netmask_admin_eth0'
assert ret[1].index() == 0
# #
cfg.option('leadership.ip_admin_eth0').value.set(['a', 'b']) cfg.option('leadership.ip_admin_eth0').value.set(['a', 'b'])
cfg.option('leadership.netmask_admin_eth0', 0).value.set('c') cfg.option('leadership.netmask_admin_eth0', 0).value.set('c')
cfg.option('leadership.netmask_admin_eth0', 1).value.set('d') cfg.option('leadership.netmask_admin_eth0', 1).value.set('d')
ret = cfg.option('leadership').list() ret = cfg.option('leadership').list('all')
assert ret[0].name() == 'ip_admin_eth0' assert ret[0].name() == 'ip_admin_eth0'
assert ret[1].name() == 'netmask_admin_eth0' assert ret[1].name() == 'netmask_admin_eth0'
# assert ret[1].option.index() == 0 # assert ret[1].option.index() == 0
@ -302,7 +303,7 @@ def test_groups_with_leader_make_dict(config_type):
od1 = OptionDescription('root', '', [interface1]) od1 = OptionDescription('root', '', [interface1])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': []} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': []}
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME useful? already in leadership # FIXME useful? already in leadership
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.len() == 0 assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.len() == 0
@ -312,7 +313,7 @@ def test_groups_with_leader_make_dict(config_type):
# FIXME # FIXME
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.len() == 2 assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.len() == 2
assert cfg.option('ip_admin_eth0.netmask_admin_eth0').value.len() == 2 assert cfg.option('ip_admin_eth0.netmask_admin_eth0').value.len() == 2
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'ip1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'ip2', 'ip_admin_eth0.netmask_admin_eth0': None}]} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'ip1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'ip2', 'ip_admin_eth0.netmask_admin_eth0': None}]}
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
# assert not list_sessions() # assert not list_sessions()
@ -335,7 +336,7 @@ def test_groups_with_leader_make_dict2(config_type):
od1 = OptionDescription('root', '', [interface1]) od1 = OptionDescription('root', '', [interface1])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'other.ip_admin_eth0': []} assert cfg.value.dict() == {'other.ip_admin_eth0': []}
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME useful? already in leadership # FIXME useful? already in leadership
assert cfg.option('other.ip_admin_eth0').value.len() == 0 assert cfg.option('other.ip_admin_eth0').value.len() == 0
@ -345,7 +346,7 @@ def test_groups_with_leader_make_dict2(config_type):
# FIXME # FIXME
assert cfg.option('other.ip_admin_eth0').value.len() == 2 assert cfg.option('other.ip_admin_eth0').value.len() == 2
assert cfg.option('other.netmask_admin_eth0').value.len() == 2 assert cfg.option('other.netmask_admin_eth0').value.len() == 2
assert parse_od_get(cfg.value.get()) == {'other.ip_admin_eth0': [{'other.ip_admin_eth0': 'ip1', 'other.netmask_admin_eth0': None}, {'other.ip_admin_eth0': 'ip2', 'other.netmask_admin_eth0': None}]} assert cfg.value.dict() == {'other.ip_admin_eth0': [{'other.ip_admin_eth0': 'ip1', 'other.netmask_admin_eth0': None}, {'other.ip_admin_eth0': 'ip2', 'other.netmask_admin_eth0': None}]}
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
# assert not list_sessions() # assert not list_sessions()
@ -410,10 +411,7 @@ def test_groups_with_leader_hidden_in_config():
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
cfg.forcepermissive.option('ip_admin_eth0.netmask_admin_eth0').index(0).value.get() assert cfg.value.dict() == {}
with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0').index(0).value.get()
assert parse_od_get(cfg.value.get()) == {}
# assert not list_sessions() # assert not list_sessions()
@ -428,13 +426,16 @@ def test_groups_with_leader_hidden_in_config2():
assert cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] assert cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.1']) cfg.forcepermissive.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.1'])
assert cfg.forcepermissive.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None assert cfg.forcepermissive.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.1'] with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': ['192.168.1.1']}
assert cfg.value.dict(leader_to_list=True) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]}
# assert not list_sessions() # assert not list_sessions()
def test_groups_with_leader_hidden_in_config3(): def test_groups_with_leader_hidden_in_config2():
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, properties=('hidden',)) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('hidden',))
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
@ -809,19 +810,6 @@ def test_values_with_leader_and_followers_leader_pop():
# assert not list_sessions() # assert not list_sessions()
def test_values_with_leader_and_followers_leader_pop_default_value():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", default=["192.168.230.145", "192.168.230.146"], multi=True, properties=('notunique',))
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", default_multi="255.255.255.0", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('toto', '', [interface1])
cfg = Config(od1)
cfg.property.read_write()
cfg.option('ip_admin_eth0.ip_admin_eth0').value.pop(0)
compare(cfg.value.exportation(), {'ip_admin_eth0.ip_admin_eth0': {None: [['192.168.230.146'], 'user']}})
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ["192.168.230.146"]
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == '255.255.255.0'
def test_follower_unique(): def test_follower_unique():
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, properties=('unique',)) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('unique',))
@ -830,7 +818,7 @@ def test_follower_unique():
cfg = Config(od1) cfg = Config(od1)
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"]) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"])
# unique property is removed for a follower # unique property is removed for a follower
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == {"validator"} assert not cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get()
# assert not list_sessions() # assert not list_sessions()
@ -1094,10 +1082,6 @@ def test_follower_properties():
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.add('newproperty1') cfg.option('ip_admin_eth0.netmask_admin_eth0').property.add('newproperty1')
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.get() == ('aproperty', 'newproperty', 'newproperty1') cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).property.get() == ('aproperty', 'newproperty', 'newproperty1')
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == ('aproperty', 'newproperty1') cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == ('aproperty', 'newproperty1')
#
with pytest.raises(ConfigError):
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.get()
cfg.option('ip_admin_eth0.netmask_admin_eth0').property.get(uncalculated=True) == ('aproperty', 'newproperty1')
# assert not list_sessions() # assert not list_sessions()
@ -1131,37 +1115,3 @@ def test_leader_forbidden_properties_callback(config_type):
cfg = Config(od1) cfg = Config(od1)
with pytest.raises(LeadershipError): with pytest.raises(LeadershipError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
def test_follower_value_not_list():
ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", default_multi='255.255.255.0', multi=True, properties=('force_store_value',))
interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('od', '', [interface0])
od2 = OptionDescription('root', '', [od1])
cfg = Config(od2)
cfg.property.read_write()
with pytest.raises(ValueError):
cfg.option('od.interface0.ip_admin_eth0').value.set(None)
def test_default_calc():
var1 = StrOption('var1', "", multi=True, default=['leader1', 'leader2'], properties=frozenset({"mandatory",}))
var2 = StrOption('var2', "", default_multi=[Calculation(calc_value, Params((ParamOption(var1))))], multi=submulti, properties=frozenset({"mandatory",}))
leader = Leadership('interface0', '', [var1, var2])
od1 = OptionDescription('od', '', [leader])
od2 = OptionDescription('root', '', [od1])
cfg = Config(od2)
assert parse_od_get(cfg.value.get()) == {'od.interface0.var1': [{'od.interface0.var1': 'leader1', 'od.interface0.var2': ['leader1']}, {'od.interface0.var1': 'leader2', 'od.interface0.var2': ['leader2']}]}
def test_default_set_calc():
var1 = StrOption('var1', "", multi=True, properties=frozenset({"mandatory", "novalidator", "force_store_value"}))
var2 = StrOption('var2', "", default_multi=[Calculation(calc_value, Params((ParamOption(var1))))], multi=submulti, properties=frozenset({"mandatory",}))
leader = Leadership('interface0', '', [var1, var2])
od1 = OptionDescription('od', '', [leader])
od2 = OptionDescription('root', '', [od1])
cfg = Config(od2)
cfg.property.read_write()
cfg.option('od.interface0.var1').value.set(Calculation(calc_value, Params(ParamValue(['leader1', 'leader2']))))
assert parse_od_get(cfg.value.get()) == {'od.interface0.var1': [{'od.interface0.var1': 'leader1', 'od.interface0.var2': ['leader1']}, {'od.interface0.var1': 'leader2', 'od.interface0.var2': ['leader2']}]}

View file

@ -1,7 +1,6 @@
# coding: utf-8 # coding: utf-8
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import parse_od_get
# FIXME from .config import config_type, get_config # FIXME from .config import config_type, get_config
import pytest import pytest
@ -13,6 +12,8 @@ from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.setting import groups from tiramisu.setting import groups
#def teardown_function(function):
# assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__)
def is_mandatory(variable): def is_mandatory(variable):
return True return True
@ -49,6 +50,18 @@ def make_description2():
return descr return descr
def make_description_sym():
stroption = StrOption('str', 'Test string option', default="abc",
properties=('mandatory', ))
stroption1 = StrOption('str1', 'Test string option',
properties=('mandatory', ))
stroption2 = SymLinkOption('unicode2', stroption1)
stroption3 = StrOption('str3', 'Test string option', multi=True,
properties=('mandatory', ))
descr = OptionDescription('tiram', '', [stroption, stroption1, stroption2, stroption3])
return descr
def make_description3(): def make_description3():
stroption = StrOption('str', 'Test string option', default="abc", stroption = StrOption('str', 'Test string option', default="abc",
properties=('mandatory', )) properties=('mandatory', ))
@ -86,7 +99,7 @@ def test_mandatory_ro_dict():
cfg.property.read_only() cfg.property.read_only()
prop = [] prop = []
try: try:
cfg.value.get() cfg.value.dict()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'mandatory' in prop assert 'mandatory' in prop
@ -95,14 +108,14 @@ def test_mandatory_ro_dict():
cfg.option('unicode2').value.set('yes') cfg.option('unicode2').value.set('yes')
cfg.property.read_only() cfg.property.read_only()
try: try:
cfg.value.get() cfg.value.dict()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'mandatory' in prop assert 'mandatory' in prop
cfg.property.read_write() cfg.property.read_write()
cfg.option('str3').value.set(['yes']) cfg.option('str3').value.set(['yes'])
cfg.property.read_only() cfg.property.read_only()
assert parse_od_get(cfg.value.get()) == {'str': 'abc', 'str1': 'yes', 'str3': ['yes'], 'unicode2': 'yes'} assert cfg.value.dict() == {'str': 'abc', 'str1': 'yes', 'str3': ['yes'], 'unicode2': 'yes'}
# assert not list_sessions() # assert not list_sessions()
@ -208,9 +221,7 @@ def test_mandatory_multi_none():
cfg.option('str3').value.get() cfg.option('str3').value.get()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'empty' in prop assert 'mandatory' in prop
assert cfg.option("str3").value.mandatory()
assert cfg.option("str3").value.mandatory(return_type=True) == "empty"
cfg.property.read_write() cfg.property.read_write()
cfg.option('str3').value.set(['yes', None]) cfg.option('str3').value.set(['yes', None])
assert cfg.option('str3').owner.get() == 'user' assert cfg.option('str3').owner.get() == 'user'
@ -220,7 +231,7 @@ def test_mandatory_multi_none():
cfg.option('str3').value.get() cfg.option('str3').value.get()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'empty' in prop assert 'mandatory' in prop
# assert not list_sessions() # assert not list_sessions()
@ -246,7 +257,7 @@ def test_mandatory_multi_empty():
cfg.option('str3').value.get() cfg.option('str3').value.get()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'empty' in prop assert 'mandatory' in prop
# #
cfg.property.read_write() cfg.property.read_write()
cfg.option('str3').value.set(['yes', '']) cfg.option('str3').value.set(['yes', ''])
@ -257,7 +268,7 @@ def test_mandatory_multi_empty():
cfg.option('str3').value.get() cfg.option('str3').value.get()
except PropertiesOptionError as err: except PropertiesOptionError as err:
prop = err.proptype prop = err.proptype
assert 'empty' in prop assert 'mandatory' in prop
# assert not list_sessions() # assert not list_sessions()
@ -334,26 +345,10 @@ def test_mandatory_warnings_ro():
prop = err.proptype prop = err.proptype
assert 'mandatory' in prop assert 'mandatory' in prop
compare(cfg.value.mandatory(), ['str', 'str1', 'unicode2', 'str3']) compare(cfg.value.mandatory(), ['str', 'str1', 'unicode2', 'str3'])
assert cfg.option('str').value.mandatory()
assert cfg.option('str1').value.mandatory()
assert cfg.option('unicode2').value.mandatory()
assert cfg.option('str3').value.mandatory()
assert cfg.option('str').value.mandatory(return_type=True) == 'mandatory'
assert cfg.option('str1').value.mandatory(return_type=True) == 'mandatory'
assert cfg.option('unicode2').value.mandatory(return_type=True) == 'mandatory'
assert cfg.option('str3').value.mandatory(return_type=True) == 'mandatory'
cfg.property.read_write() cfg.property.read_write()
cfg.option('str').value.set('a') cfg.option('str').value.set('a')
cfg.property.read_only() cfg.property.read_only()
compare(cfg.value.mandatory(), ['str1', 'unicode2', 'str3']) compare(cfg.value.mandatory(), ['str1', 'unicode2', 'str3'])
assert not cfg.option('str').value.mandatory()
assert cfg.option('str1').value.mandatory()
assert cfg.option('unicode2').value.mandatory()
assert cfg.option('str3').value.mandatory()
assert cfg.option('str').value.mandatory(return_type=True) is False
assert cfg.option('str1').value.mandatory(return_type=True) == 'mandatory'
assert cfg.option('unicode2').value.mandatory(return_type=True) == 'mandatory'
assert cfg.option('str3').value.mandatory(return_type=True) == 'mandatory'
# assert not list_sessions() # assert not list_sessions()
@ -419,7 +414,7 @@ def test_mandatory_leader():
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -436,7 +431,7 @@ def test_mandatory_leader_sub():
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('o.ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('o.ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -534,7 +529,7 @@ def test_mandatory_follower():
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == []
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': []} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': []}
# #
cfg.property.read_write() cfg.property.read_write()
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['ip']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['ip'])
@ -555,7 +550,7 @@ def test_mandatory_follower():
cfg.property.read_only() cfg.property.read_only()
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['ip'] assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['ip']
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == 'ip' assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == 'ip'
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'ip', 'ip_admin_eth0.netmask_admin_eth0': 'ip'}]} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'ip', 'ip_admin_eth0.netmask_admin_eth0': 'ip'}]}
# assert not list_sessions() # assert not list_sessions()
@ -577,14 +572,7 @@ def test_mandatory_warnings_follower():
def test_mandatory_warnings_symlink(): def test_mandatory_warnings_symlink():
stroption = StrOption('str', 'Test string option', default="abc", od1 = make_description_sym()
properties=('mandatory', ))
stroption1 = StrOption('str1', 'Test string option',
properties=('mandatory', ))
stroption2 = SymLinkOption('unicode2', stroption1)
stroption3 = StrOption('str3', 'Test string option', multi=True,
properties=('mandatory', ))
od1 = OptionDescription('tiram', '', [stroption, stroption1, stroption2, stroption3])
cfg = Config(od1) cfg = Config(od1)
cfg.option('str').value.set('') cfg.option('str').value.set('')
cfg.property.read_write() cfg.property.read_write()
@ -697,7 +685,7 @@ def test_mandatory_od_disabled():
# assert not list_sessions() # assert not list_sessions()
def return_list(val=None, identifier=None): def return_list(val=None, suffix=None):
if val: if val:
return val return val
else: else:
@ -706,7 +694,7 @@ def return_list(val=None, identifier=None):
def test_mandatory_dyndescription(): def test_mandatory_dyndescription():
st = StrOption('st', '', properties=('mandatory',)) st = StrOption('st', '', properties=('mandatory',))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list)) dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
od = OptionDescription('od', '', [dod]) od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
@ -717,7 +705,7 @@ def test_mandatory_dyndescription():
def test_mandatory_dyndescription_context(): def test_mandatory_dyndescription_context():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', properties=('mandatory',)) st = StrOption('st', '', properties=('mandatory',))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1)))) dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list, Params(ParamOption(val1))))
od = OptionDescription('od', '', [dod, val1]) od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
@ -745,16 +733,4 @@ def test_mandatory_and_disabled():
od1 = OptionDescription('rootconfig', '', [username, password]) od1 = OptionDescription('rootconfig', '', [username, password])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.value.get() cfg.value.dict()
def test_mandatory_permissive():
val1 = StrOption('val1', "", properties=('mandatory', 'hidden'))
od2 = OptionDescription('val1', '', [val1])
od1 = OptionDescription('rootconfig', '', [od2])
cfg = Config(od1)
cfg.property.read_write()
compare(cfg.value.mandatory(set_permissive=False), [])
compare(cfg.value.mandatory(), ['val1.val1'])
compare(cfg.option("val1").value.mandatory(set_permissive=False), [])
compare(cfg.option("val1").value.mandatory(), ['val1.val1'])

View file

@ -4,11 +4,11 @@ do_autopath()
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, BoolOption, ChoiceOption, \ from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, BoolOption, ChoiceOption, \
IPOption, OptionDescription, DynOptionDescription, Leadership, Config, GroupConfig, MetaConfig, \ IPOption, OptionDescription, Leadership, Config, GroupConfig, MetaConfig, \
Calculation, Params, ParamOption, ParamValue, calc_value, ParamSelfOption, \ Calculation, Params, ParamOption, ParamValue, calc_value, ParamSelfOption, \
valid_network_netmask, valid_not_equal valid_network_netmask, valid_not_equal
from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError
from .config import config_type, get_config, parse_od_get from .config import config_type, get_config
owners.addowner('config') owners.addowner('config')
@ -50,13 +50,7 @@ def make_metaconfig(double=False):
conf2.property.read_write() conf2.property.read_write()
meta = MetaConfig([conf1, conf2], name='meta') meta = MetaConfig([conf1, conf2], name='meta')
assert meta.config.type() == 'metaconfig' assert meta.config.type() == 'metaconfig'
assert meta.type() == 'metaconfig'
assert meta.config.name() == 'meta' assert meta.config.name() == 'meta'
assert meta.name() == 'meta'
assert conf1.config.type() == 'config'
assert conf1.type() == 'config'
assert conf1.config.name() == 'conf1'
assert conf1.name() == 'conf1'
if double: if double:
meta.owner.set(owners.meta2) meta.owner.set(owners.meta2)
meta = MetaConfig([meta], name='doublemeta') meta = MetaConfig([meta], name='doublemeta')
@ -93,14 +87,10 @@ def test_none():
conf2 = meta.config('conf2') conf2 = meta.config('conf2')
assert meta.option('od1.i3').value.get() is conf1.option('od1.i3').value.get() is conf2.option('od1.i3').value.get() is None assert meta.option('od1.i3').value.get() is conf1.option('od1.i3').value.get() is conf2.option('od1.i3').value.get() is None
assert meta.option('od1.i3').owner.get() is conf1.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.default assert meta.option('od1.i3').owner.get() is conf1.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.default
assert meta.option('od1.i3').owner.isdefault() and conf1.option('od1.i3').owner.isdefault()
# #
#assert meta.option('od1.i3').value.set(3) == [] assert meta.option('od1.i3').value.set(3) == []
meta.option('od1.i3').value.set(3)
assert meta.option('od1.i3').value.get() == conf1.option('od1.i3').value.get() == conf2.option('od1.i3').value.get() == 3 assert meta.option('od1.i3').value.get() == conf1.option('od1.i3').value.get() == conf2.option('od1.i3').value.get() == 3
assert meta.option('od1.i3').owner.get() is conf1.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.meta1 assert meta.option('od1.i3').owner.get() is conf1.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.meta1
assert not meta.option('od1.i3').owner.isdefault()
assert conf1.option('od1.i3').owner.isdefault()
# #
conf1.option('od1.i3').value.set(2) conf1.option('od1.i3').value.set(2)
assert meta.option('od1.i3').value.get() == conf2.option('od1.i3').value.get() == 3 assert meta.option('od1.i3').value.get() == conf2.option('od1.i3').value.get() == 3
@ -119,8 +109,6 @@ def test_none():
assert conf1.option('od1.i3').value.get() == 2 assert conf1.option('od1.i3').value.get() == 2
assert meta.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.default assert meta.option('od1.i3').owner.get() is conf2.option('od1.i3').owner.get() is owners.default
assert conf1.option('od1.i3').owner.get() is owners.user assert conf1.option('od1.i3').owner.get() is owners.user
assert meta.option("od1.i3").owner.default() is owners.default
assert conf1.option("od1.i3").owner.default() is owners.default
# #
conf1.option('od1.i3').value.reset() conf1.option('od1.i3').value.reset()
assert meta.option('od1.i3').value.get() is conf1.option('od1.i3').value.get() is conf2.option('od1.i3').value.get() is None assert meta.option('od1.i3').value.get() is conf1.option('od1.i3').value.get() is conf2.option('od1.i3').value.get() is None
@ -191,6 +179,17 @@ def test_contexts():
assert conf1.option('od1.i2').owner.get() == conf1.option('od1.i2').owner.get() is owners.user assert conf1.option('od1.i2').owner.get() == conf1.option('od1.i2').owner.get() is owners.user
def test_find():
meta = make_metaconfig()
ret = list(meta.option.find('i2'))
assert len(ret) == 1
assert 1 == ret[0].value.get()
ret = meta.option.find('i2', first=True)
assert 1 == ret.value.get()
assert meta.value.dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None,
'od1.i2': 1, 'od1.i5': [2]}
def test_meta_meta(): def test_meta_meta():
meta = make_metaconfig(double=True) meta = make_metaconfig(double=True)
meta1 = meta.config('meta') meta1 = meta.config('meta')
@ -270,7 +269,7 @@ def test_meta_pop_config():
assert len(list(meta.config.list())) == 2 assert len(list(meta.config.list())) == 2
meta.config.new('newconf1') meta.config.new('newconf1')
newconf1 = meta.config('newconf1') newconf1 = meta.config('newconf1')
assert parse_od_get(newconf1.value.get()) == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert newconf1.value.dict() == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
# #
assert len(list(meta.config.list())) == 3 assert len(list(meta.config.list())) == 3
newconf1 = meta.config.remove('newconf1') newconf1 = meta.config.remove('newconf1')
@ -280,7 +279,7 @@ def test_meta_pop_config():
pass pass
else: else:
raise Exception('must raise') raise Exception('must raise')
assert parse_od_get(newconf1.value.get()) == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert newconf1.value.dict() == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
# #
assert len(list(meta.config.list())) == 2 assert len(list(meta.config.list())) == 2
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
@ -295,11 +294,11 @@ def test_meta_add_config():
# #
assert len(list(meta.config.list())) == 2 assert len(list(meta.config.list())) == 2
config = Config(od, name='new') config = Config(od, name='new')
assert parse_od_get(config.value.get()) == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert config.value.dict() == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
meta.config.add(config) meta.config.add(config)
# #
assert len(list(meta.config.list())) == 3 assert len(list(meta.config.list())) == 3
assert parse_od_get(config.value.get()) == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert config.value.dict() == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
# #
with pytest.raises(ConflictError): with pytest.raises(ConflictError):
meta.config.add(config) meta.config.add(config)
@ -365,9 +364,38 @@ def test_meta_meta_set():
conf2 = meta.config('meta.conf2') conf2 = meta.config('meta.conf2')
assert conf1.option('od1.i1').value.get() == conf2.option('od1.i1').value.get() == 7 assert conf1.option('od1.i1').value.get() == conf2.option('od1.i1').value.get() == 7
# #
dconfigs = []
ret = meta.config.find('i1', value=7)
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1._config_bag.context, conf2._config_bag.context] == dconfigs
conf1.option('od1.i1').value.set(8) conf1.option('od1.i1').value.set(8)
assert conf1.option('od1.i1').value.get() == 8 #
assert conf2.option('od1.i1').value.get() == 7 dconfigs = []
ret = meta.config.find('i1')
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1._config_bag.context, conf2._config_bag.context] == dconfigs
ret = meta.config.find('i1', value=7)
assert len(ret.config.list()) == 1
assert conf2._config_bag.context == list(ret.config.list())[0]._config_bag.context
ret = meta.config.find('i1', value=8)
assert len(ret.config.list()) == 1
assert conf1._config_bag.context == list(ret.config.list())[0]._config_bag.context
#
dconfigs = []
ret = meta.config.find('i5', value=2)
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1._config_bag.context, conf2._config_bag.context] == dconfigs
#
with pytest.raises(AttributeError):
meta.config.find('i1', value=10)
with pytest.raises(AttributeError):
meta.config.find('not', value=10)
with pytest.raises(AttributeError):
meta.config.find('i6')
with pytest.raises(ValueError): with pytest.raises(ValueError):
meta.value.set('od1.i6', 7, only_config=True, force_default=True) meta.value.set('od1.i6', 7, only_config=True, force_default=True)
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -399,7 +427,7 @@ def test_not_meta():
conf2 = grp.config('conf2') conf2 = grp.config('conf2')
assert conf1.option('od1.i1').value.get() == conf2.option('od1.i1').value.get() == 7 assert conf1.option('od1.i1').value.get() == conf2.option('od1.i1').value.get() == 7
assert conf1.option('od1.i1').owner.get() is conf2.option('od1.i1').owner.get() is owners.user assert conf1.option('od1.i1').owner.get() is conf2.option('od1.i1').owner.get() is owners.user
grp.value.reset('od1.i1') grp.option('od1.i1').value.reset()
assert conf1.option('od1.i1').owner.get() is conf2.option('od1.i1').owner.get() is owners.default assert conf1.option('od1.i1').owner.get() is conf2.option('od1.i1').owner.get() is owners.default
@ -410,6 +438,7 @@ def test_group_find_firsts():
conf1 = Config(od2, name='conf1') conf1 = Config(od2, name='conf1')
conf2 = Config(od2, name='conf2') conf2 = Config(od2, name='conf2')
grp = GroupConfig([conf1, conf2]) grp = GroupConfig([conf1, conf2])
ret = grp.config.find('i1')
newconf1, newconf2 = grp.config.list() newconf1, newconf2 = grp.config.list()
conf1._config_bag.context == newconf1._config_bag.context conf1._config_bag.context == newconf1._config_bag.context
conf2._config_bag.context == newconf2._config_bag.context conf2._config_bag.context == newconf2._config_bag.context
@ -476,26 +505,36 @@ def test_meta_unconsistent():
def test_meta_leadership(): def test_meta_leadership():
leader = StrOption('leader', "", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip", multi=True)
follower = StrOption('follower', "", multi=True) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "mask", multi=True, properties=('hidden',))
interface1 = Leadership('leadership', '', [leader, follower]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [interface1]) od = OptionDescription('root', '', [interface1])
conf1 = Config(od, name='conf1') conf1 = Config(od, name='conf1')
conf2 = Config(od, name='conf2') conf2 = Config(od, name='conf2')
meta = MetaConfig([conf1, conf2]) meta = MetaConfig([conf1, conf2])
meta.property.read_only()
ret = meta.config.find('ip_admin_eth0')
configs = ret.config.list()
assert len(configs) == 2
assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf2._config_bag.context == configs[1]._config_bag.context
ret = meta.config.find('netmask_admin_eth0')
configs = ret.config.list()
assert len(configs) == 2
assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf2._config_bag.context == configs[1]._config_bag.context
meta.property.read_write() meta.property.read_write()
# with pytest.raises(AttributeError):
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == [] meta.config.find('netmask_admin_eth0')
# ret = meta.unrestraint.config.find('netmask_admin_eth0')
meta.option('leadership.leader').value.set(["val1"]) configs = ret.config.list()
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == ['val1'] assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf1.option('leadership.follower', 0).value.get() == conf2.option('leadership.follower', 0).value.get() == None assert conf2._config_bag.context == configs[1]._config_bag.context
# meta.property.read_only()
meta.option('leadership.leader').value.set(["val1", "val2"]) ret = meta.config.find('netmask_admin_eth0')
meta.option('leadership.follower', 0).value.set("val3") configs = ret.config.list()
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == ['val1', 'val2'] assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf1.option('leadership.follower', 0).value.get() == conf2.option('leadership.follower', 0).value.get() == "val3" assert conf2._config_bag.context == configs[1]._config_bag.context
assert conf1.option('leadership.follower', 1).value.get() == conf2.option('leadership.follower', 1).value.get() == None
def test_meta_leadership_value(): def test_meta_leadership_value():
@ -775,7 +814,7 @@ def test_meta_properties_meta():
meta = MetaConfig([conf1, conf2]) meta = MetaConfig([conf1, conf2])
meta.property.read_write() meta.property.read_write()
ret = meta.config('conf1') ret = meta.config('conf1')
assert parse_od_get(ret.value.get()) == {} assert ret.value.dict() == {}
def test_meta_exception_meta(): def test_meta_exception_meta():
@ -831,7 +870,7 @@ def test_meta_properties_requires_mandatory():
meta.option('ip_gw').value.set('1.1.1.2') meta.option('ip_gw').value.set('1.1.1.2')
conf1.option('eth0_method').value.set('dhcp') conf1.option('eth0_method').value.set('dhcp')
conf1.property.read_only() conf1.property.read_only()
assert parse_od_get(conf1.value.get()) == {'ip_gw': '1.1.1.2', 'probes': True, 'eth0_method': 'dhcp', 'ip_address': '1.1.1.1', 'ip_eth0': '1.1.1.1'} assert conf1.value.dict() == {'ip_gw': '1.1.1.2', 'probes': True, 'eth0_method': 'dhcp', 'ip_address': '1.1.1.1', 'ip_eth0': '1.1.1.1'}
def test_meta_callback(): def test_meta_callback():
@ -845,17 +884,17 @@ def test_meta_callback():
meta = MetaConfig([cfg]) meta = MetaConfig([cfg])
meta.property.read_write() meta.property.read_write()
newcfg = meta.config('cfg') newcfg = meta.config('cfg')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'val', 'val1': 'val', 'val5': 'yes', 'val4': 'val'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'val', 'val1': 'val', 'val5': 'yes', 'val4': 'val'}
newcfg.option('val1').value.set('new') newcfg.option('val1').value.set('new')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'}
newcfg.option('val1').value.reset() newcfg.option('val1').value.reset()
meta.option('val1').value.set('new') meta.option('val1').value.set('new')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'}
newcfg.option('val4').value.set('new1') newcfg.option('val4').value.set('new1')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'}
newcfg.option('val4').value.reset() newcfg.option('val4').value.reset()
meta.option('val4').value.set('new1') meta.option('val4').value.set('new1')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'}
meta.option('val4').value.reset() meta.option('val4').value.reset()
@ -870,51 +909,51 @@ def test_meta_callback_follower():
conf1 = Config(maconfig, name='conf1') conf1 = Config(maconfig, name='conf1')
meta = MetaConfig([conf1]) meta = MetaConfig([conf1])
meta.property.read_write() meta.property.read_write()
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
conf1.option('val').value.set('val1') conf1.option('val').value.set('val1')
assert parse_od_get(conf1.value.get()) == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]} assert conf1.value.dict() == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]}
# #
conf1.option('val').value.reset() conf1.option('val').value.reset()
meta.option('val').value.set('val1') meta.option('val').value.set('val1')
assert parse_od_get(conf1.value.get()) == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]} assert conf1.value.dict() == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]}
# #
meta.option('val').value.reset() meta.option('val').value.reset()
conf1.option('val1.val2', 0).value.set('val2') conf1.option('val1.val2', 0).value.set('val2')
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
conf1.option('val1.val2', 0).value.reset() conf1.option('val1.val2', 0).value.reset()
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
meta.option('val1.val2', 0).value.set('val2') meta.option('val1.val2', 0).value.set('val2')
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
meta.option('val1.val1').value.set(['val']) meta.option('val1.val1').value.set(['val'])
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
conf1.option('val1.val3', 0).value.set('val6') conf1.option('val1.val3', 0).value.set('val6')
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val6'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val6'}]}
# #
meta.option('val1.val2', 0).value.reset() meta.option('val1.val2', 0).value.reset()
conf1.option('val1.val3', 0).value.reset() conf1.option('val1.val3', 0).value.reset()
conf1.option('val1.val1').value.set(['val3']) conf1.option('val1.val1').value.set(['val3'])
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]}
# #
conf1.option('val1.val1').value.reset() conf1.option('val1.val1').value.reset()
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
meta.option('val1.val1').value.set(['val3']) meta.option('val1.val1').value.set(['val3'])
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]}
# #
conf1.option('val1.val2', 0).value.set('val2') conf1.option('val1.val2', 0).value.set('val2')
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}]}
# #
meta.option('val1.val1').value.set(['val3', 'rah']) meta.option('val1.val1').value.set(['val3', 'rah'])
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}, {'val1.val1': 'rah', 'val1.val2': 'rah', 'val1.val3': 'rah'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}, {'val1.val1': 'rah', 'val1.val2': 'rah', 'val1.val3': 'rah'}]}
# #
meta.option('val1.val1').value.pop(1) meta.option('val1.val1').value.pop(1)
meta.option('val1.val1').value.set(['val4']) meta.option('val1.val1').value.set(['val4'])
assert parse_od_get(conf1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val4', 'val1.val2': 'val2', 'val1.val3': 'val4'}]} assert conf1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val4', 'val1.val2': 'val2', 'val1.val3': 'val4'}]}
def test_meta_reset(): def test_meta_reset():
@ -965,18 +1004,18 @@ def test_meta_properties_meta_copy():
assert len(meta2) == 1 assert len(meta2) == 1
assert meta.config.name() == meta2[0].config.name() assert meta.config.name() == meta2[0].config.name()
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
newconf3 = meta.config('conf3') newconf3 = meta.config('conf3')
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
meta.option('ip_admin_eth0').value.set(['192.168.1.2']) meta.option('ip_admin_eth0').value.set(['192.168.1.2'])
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
ret = meta.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True) ret = meta.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True)
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
def test_meta_properties_meta_deepcopy(): def test_meta_properties_meta_deepcopy():
@ -999,17 +1038,17 @@ def test_meta_properties_meta_deepcopy():
assert meta != meta2 assert meta != meta2
assert meta.permissive.get() == meta2.permissive.get() assert meta.permissive.get() == meta2.permissive.get()
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
meta.option('ip_admin_eth0').value.set(['192.168.1.2']) meta.option('ip_admin_eth0').value.set(['192.168.1.2'])
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
meta.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True) meta.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True)
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
def test_meta_properties_meta_deepcopy_multi_parent(): def test_meta_properties_meta_deepcopy_multi_parent():
@ -1030,17 +1069,17 @@ def test_meta_properties_meta_deepcopy_multi_parent():
meta1.option('ip_admin_eth0').value.set('192.168.1.1') meta1.option('ip_admin_eth0').value.set('192.168.1.1')
meta2.option('netmask_admin_eth0').value.set('255.255.255.0') meta2.option('netmask_admin_eth0').value.set('255.255.255.0')
assert parse_od_get(meta1.value.get()) == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None} assert meta1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None}
assert parse_od_get(meta2.value.get()) == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'} assert meta2.value.dict() == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'}
assert parse_od_get(conf1.value.get()) == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'} assert conf1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None} assert conf2.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None}
copy_meta2 = conf1.config.deepcopy(name='copy_conf1', metaconfig_prefix='copy_') copy_meta2 = conf1.config.deepcopy(name='copy_conf1', metaconfig_prefix='copy_')
assert copy_meta2.config.path() == 'copy_meta2' assert copy_meta2.config.path() == 'copy_meta2'
copy_meta1 = copy_meta2.config('copy_meta1') copy_meta1 = copy_meta2.config('copy_meta1')
copy_conf1 = copy_meta1.config('copy_conf1') copy_conf1 = copy_meta1.config('copy_conf1')
assert parse_od_get(copy_meta2.value.get()) == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'} assert copy_meta2.value.dict() == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'}
assert parse_od_get(copy_conf1.value.get()) == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'} assert copy_conf1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'}
def test_meta_properties_submeta_deepcopy(): def test_meta_properties_submeta_deepcopy():
@ -1143,7 +1182,7 @@ def test_meta_properties_meta_set_value():
meta = MetaConfig([conf1, conf2]) meta = MetaConfig([conf1, conf2])
meta.property.read_write() meta.property.read_write()
ret = meta.config('conf1') ret = meta.config('conf1')
assert parse_od_get(ret.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert ret.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
ret = meta.value.set('netmask_admin_eth0', ['255.255.255.255'], only_config=True) ret = meta.value.set('netmask_admin_eth0', ['255.255.255.255'], only_config=True)
assert len(ret) == 2 assert len(ret) == 2
assert isinstance(ret[0], PropertiesOptionError) assert isinstance(ret[0], PropertiesOptionError)
@ -1306,26 +1345,3 @@ def test_meta_get_config():
assert isinstance(meta.config('name1'), Config) assert isinstance(meta.config('name1'), Config)
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
meta.config('unknown') meta.config('unknown')
def test_meta_dyndescription():
lst = StrOption('lst', '', multi=True)
st = StrOption('st', '')
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_value, Params(ParamOption(lst))))
od = OptionDescription('od', '', [dod, lst])
cfg = Config(od, name="cfg")
cfg.owner.set(owners.config)
meta1 = MetaConfig([cfg], name='meta1')
meta1.owner.set(owners.meta1)
meta2 = MetaConfig([meta1], name='meta2')
meta2.owner.set(owners.meta2)
cfg.property.read_write()
meta2.option('lst').value.set(["val1", "val2"])
meta1.option('lst').value.set(["val3", "val4"])
cfg.option('lst').value.set(["val5", "val6"])
assert cfg.option('dodval6.st').value.get() is None
with pytest.raises(AttributeError):
meta1.option('dodval6.st').value.get()
assert parse_od_get(cfg.value.get()) == {'dodval5.st': None, 'dodval6.st': None, 'lst': ['val5', 'val6']}
assert parse_od_get(meta1.value.get()) == {'dodval3.st': None, 'dodval4.st': None, 'lst': ['val3', 'val4']}
assert parse_od_get(meta2.value.get()) == {'dodval1.st': None, 'dodval2.st': None, 'lst': ['val1', 'val2']}

View file

@ -9,7 +9,6 @@ from tiramisu import IntOption, StrOption, NetworkOption, NetmaskOption, \
MetaConfig, Params, ParamOption, ParamValue, ParamSelfOption, Calculation, \ MetaConfig, Params, ParamOption, ParamValue, ParamSelfOption, Calculation, \
valid_network_netmask valid_network_netmask
from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError from tiramisu.error import ConfigError, ConflictError, PropertiesOptionError, LeadershipError
from .config import parse_od_get
owners.addowner('mix1') owners.addowner('mix1')
owners.addowner('mix2') owners.addowner('mix2')
@ -213,18 +212,18 @@ def test_contexts():
assert newconf1.option('od1.i2').value.get() == newconf1.option('od1.i2').value.get() == 6 assert newconf1.option('od1.i2').value.get() == newconf1.option('od1.i2').value.get() == 6
assert newconf1.option('od1.i2').owner.get() == newconf1.option('od1.i2').owner.get() is owners.user assert newconf1.option('od1.i2').owner.get() == newconf1.option('od1.i2').owner.get() is owners.user
assert len(errors) == 0 assert len(errors) == 0
#
#
#
#def test_find(): def test_find():
# mix = make_mixconfig() mix = make_mixconfig()
# ret = list(mix.option.find('i2')) ret = list(mix.option.find('i2'))
# assert len(ret) == 1 assert len(ret) == 1
# assert 1 == ret[0].value.get() assert 1 == ret[0].value.get()
# ret = mix.option.find('i2', first=True) ret = mix.option.find('i2', first=True)
# assert 1 == ret.value.get() assert 1 == ret.value.get()
# assert parse_od_get(mix.value.get()) == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, assert mix.value.dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None,
# 'od1.i2': 1, 'od1.i5': [2]} 'od1.i2': 1, 'od1.i5': [2]}
def test_mix_mix(): def test_mix_mix():
@ -284,10 +283,35 @@ def test_mix_mix_set():
newconf2 = mix.config('mix.conf2') newconf2 = mix.config('mix.conf2')
assert newconf1.option('od1.i1').value.get() == newconf2.option('od1.i1').value.get() == 7 assert newconf1.option('od1.i1').value.get() == newconf2.option('od1.i1').value.get() == 7
# #
dconfigs = []
ret = mix.config.find('i1', value=7)
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1, conf2] == dconfigs
newconf1.option('od1.i1').value.set(8) newconf1.option('od1.i1').value.set(8)
assert newconf1.option('od1.i1').value.get() == 8
assert newconf2.option('od1.i1').value.get() == 7
# #
dconfigs = []
ret = mix.config.find('i1')
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1, conf2] == dconfigs
ret = mix.config.find('i1', value=7)
assert conf2 == list(ret.config.list())[0]._config_bag.context
ret = mix.config.find('i1', value=8)
assert conf1 == list(ret.config.list())[0]._config_bag.context
#
dconfigs = []
ret = mix.config.find('i5', value=2)
for conf in ret.config.list():
dconfigs.append(conf._config_bag.context)
assert [conf1, conf2] == dconfigs
#
with pytest.raises(AttributeError):
mix.config.find('i1', value=10)
with pytest.raises(AttributeError):
mix.config.find('not', value=10)
with pytest.raises(AttributeError):
mix.config.find('i6')
with pytest.raises(ValueError): with pytest.raises(ValueError):
mix.value.set('od1.i6', 7, only_config=True, force_default=True) mix.value.set('od1.i6', 7, only_config=True, force_default=True)
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -325,26 +349,38 @@ def test_mix_unconsistent():
def test_mix_leadership(): def test_mix_leadership():
leader = StrOption('leader', "ip", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip", multi=True)
follower = StrOption('follower', "mask", multi=True) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "mask", multi=True, properties=('hidden',))
leadership = Leadership('leadership', '', [leader, follower]) interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od = OptionDescription('root', '', [leadership]) od = OptionDescription('root', '', [interface1])
conf1 = Config(od, name='conf1') conf1 = Config(od, name='conf1')
conf2 = Config(od, name='conf2') conf2 = Config(od, name='conf2')
mix = MixConfig(od, [conf1, conf2]) mix = MixConfig(od, [conf1, conf2])
mix.property.read_only()
ret = mix.config.find('ip_admin_eth0')
configs = ret.config.list()
assert len(configs) == 2
assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf2._config_bag.context == configs[1]._config_bag.context
ret = mix.config.find('netmask_admin_eth0')
configs = ret.config.list()
assert len(configs) == 2
assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf2._config_bag.context == configs[1]._config_bag.context
mix.property.read_write() mix.property.read_write()
# with pytest.raises(AttributeError):
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == [] mix.config.find('netmask_admin_eth0')
# ret = mix.unrestraint.config.find('netmask_admin_eth0')
mix.option('leadership.leader').value.set(["val1"]) configs = ret.config.list()
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == ['val1'] assert len(configs) == 2
assert conf1.option('leadership.follower', 0).value.get() == conf2.option('leadership.follower', 0).value.get() == None assert conf1._config_bag.context == configs[0]._config_bag.context
# assert conf2._config_bag.context == configs[1]._config_bag.context
mix.option('leadership.leader').value.set(["val1", "val2"]) mix.property.read_only()
mix.option('leadership.follower', 0).value.set("val3") ret = mix.config.find('netmask_admin_eth0')
assert conf1.option('leadership.leader').value.get() == conf2.option('leadership.leader').value.get() == ['val1', 'val2'] configs = ret.config.list()
assert conf1.option('leadership.follower', 0).value.get() == conf2.option('leadership.follower', 0).value.get() == "val3" assert len(configs) == 2
assert conf1.option('leadership.follower', 1).value.get() == conf2.option('leadership.follower', 1).value.get() == None assert conf1._config_bag.context == configs[0]._config_bag.context
assert conf2._config_bag.context == configs[1]._config_bag.context
def test_mix_leadership_value2(): def test_mix_leadership_value2():
@ -619,7 +655,7 @@ def test_mix_properties_mix():
mix = MixConfig(od, [conf1, conf2]) mix = MixConfig(od, [conf1, conf2])
mix.property.read_write() mix.property.read_write()
newconf1 = mix.config('conf1') newconf1 = mix.config('conf1')
assert parse_od_get(newconf1.value.get()) == {} assert newconf1.value.dict() == {}
def test_mix_exception_mix(): def test_mix_exception_mix():
@ -632,7 +668,7 @@ def test_mix_exception_mix():
mix = MixConfig(od, [conf1, conf2]) mix = MixConfig(od, [conf1, conf2])
mix.property.read_write() mix.property.read_write()
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
parse_od_get(conf1.value.get()) conf1.value.dict()
@ -647,17 +683,17 @@ def test_mix_callback():
mix = MixConfig(maconfig, [cfg]) mix = MixConfig(maconfig, [cfg])
mix.property.read_write() mix.property.read_write()
newcfg = mix.config('cfg') newcfg = mix.config('cfg')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'val', 'val1': 'val', 'val5': 'yes', 'val4': 'val'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'val', 'val1': 'val', 'val5': 'yes', 'val4': 'val'}
newcfg.option('val1').value.set('new') newcfg.option('val1').value.set('new')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'}
newcfg.option('val1').value.reset() newcfg.option('val1').value.reset()
mix.option('val1').value.set('new') mix.option('val1').value.set('new')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new'}
newcfg.option('val4').value.set('new1') newcfg.option('val4').value.set('new1')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'}
newcfg.option('val4').value.reset() newcfg.option('val4').value.reset()
mix.option('val4').value.set('new1') mix.option('val4').value.set('new1')
assert parse_od_get(newcfg.value.get()) == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'} assert newcfg.value.dict() == {'val3': 'yes', 'val2': 'new', 'val1': 'new', 'val5': 'yes', 'val4': 'new1'}
mix.option('val4').value.reset() mix.option('val4').value.reset()
@ -673,51 +709,51 @@ def test_mix_callback_follower():
mix = MixConfig(maconfig, [cfg]) mix = MixConfig(maconfig, [cfg])
mix.property.read_write() mix.property.read_write()
newcfg1 = mix.config('cfg1') newcfg1 = mix.config('cfg1')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
newcfg1.option('val').value.set('val1') newcfg1.option('val').value.set('val1')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]} assert newcfg1.value.dict() == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]}
# #
newcfg1.option('val').value.reset() newcfg1.option('val').value.reset()
mix.option('val').value.set('val1') mix.option('val').value.set('val1')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]} assert newcfg1.value.dict() == {'val': 'val1', 'val1.val1': [{'val1.val1': 'val1', 'val1.val2': 'val1', 'val1.val3': 'val1'}]}
# #
mix.option('val').value.reset() mix.option('val').value.reset()
newcfg1.option('val1.val2', 0).value.set('val2') newcfg1.option('val1.val2', 0).value.set('val2')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
newcfg1.option('val1.val2', 0).value.reset() newcfg1.option('val1.val2', 0).value.reset()
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
mix.option('val1.val2', 0).value.set('val2') mix.option('val1.val2', 0).value.set('val2')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
mix.option('val1.val1').value.set(['val']) mix.option('val1.val1').value.set(['val'])
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val'}]}
# #
newcfg1.option('val1.val3', 0).value.set('val6') newcfg1.option('val1.val3', 0).value.set('val6')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val6'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val2', 'val1.val3': 'val6'}]}
# #
mix.option('val1.val2', 0).value.reset() mix.option('val1.val2', 0).value.reset()
newcfg1.option('val1.val3', 0).value.reset() newcfg1.option('val1.val3', 0).value.reset()
newcfg1.option('val1.val1').value.set(['val3']) newcfg1.option('val1.val1').value.set(['val3'])
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]}
# #
newcfg1.option('val1.val1').value.reset() newcfg1.option('val1.val1').value.reset()
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val', 'val1.val2': 'val', 'val1.val3': 'val'}]}
# #
mix.option('val1.val1').value.set(['val3']) mix.option('val1.val1').value.set(['val3'])
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val3', 'val1.val3': 'val3'}]}
# #
newcfg1.option('val1.val2', 0).value.set('val2') newcfg1.option('val1.val2', 0).value.set('val2')
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}]}
# #
mix.option('val1.val1').value.set(['val3', 'rah']) mix.option('val1.val1').value.set(['val3', 'rah'])
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}, {'val1.val1': 'rah', 'val1.val2': 'rah', 'val1.val3': 'rah'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val3', 'val1.val2': 'val2', 'val1.val3': 'val3'}, {'val1.val1': 'rah', 'val1.val2': 'rah', 'val1.val3': 'rah'}]}
# #
mix.option('val1.val1').value.pop(1) mix.option('val1.val1').value.pop(1)
mix.option('val1.val1').value.set(['val4']) mix.option('val1.val1').value.set(['val4'])
assert parse_od_get(newcfg1.value.get()) == {'val': 'val', 'val1.val1': [{'val1.val1': 'val4', 'val1.val2': 'val2', 'val1.val3': 'val4'}]} assert newcfg1.value.dict() == {'val': 'val', 'val1.val1': [{'val1.val1': 'val4', 'val1.val2': 'val2', 'val1.val3': 'val4'}]}
def test_meta_reset(): def test_meta_reset():
@ -783,17 +819,17 @@ def test_mix_properties_mix_copy():
assert mix.config.name() == mix2[0].config.name() assert mix.config.name() == mix2[0].config.name()
newconf2 = mix.config('conf2') newconf2 = mix.config('conf2')
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert conf2.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
mix.option('ip_admin_eth0').value.set(['192.168.1.2']) mix.option('ip_admin_eth0').value.set(['192.168.1.2'])
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert conf2.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
ret = mix.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True) ret = mix.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True)
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert conf2.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
def test_mix_properties_mix_deepcopy(): def test_mix_properties_mix_deepcopy():
@ -824,17 +860,17 @@ def test_mix_properties_mix_deepcopy():
assert mix != mix2 assert mix != mix2
assert mix.permissive.get() == mix2.permissive.get() assert mix.permissive.get() == mix2.permissive.get()
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
mix.option('ip_admin_eth0').value.set(['192.168.1.2']) mix.option('ip_admin_eth0').value.set(['192.168.1.2'])
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.2']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
mix.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True) mix.value.set('ip_admin_eth0', ['192.168.1.3'], force_default_if_same=True)
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.3']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.3']}
assert parse_od_get(newconf3.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf3.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
def test_mix_properties_submix_deepcopy(): def test_mix_properties_submix_deepcopy():
@ -921,7 +957,7 @@ def test_mix_properties_mix_set_value():
mix = MixConfig(interface2, [conf1, conf2]) mix = MixConfig(interface2, [conf1, conf2])
mix.property.read_write() mix.property.read_write()
newconf2 = mix.config('conf2') newconf2 = mix.config('conf2')
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf2.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
ret = mix.value.set('netmask_admin_eth0', ['255.255.255.255'], only_config=True) ret = mix.value.set('netmask_admin_eth0', ['255.255.255.255'], only_config=True)
assert len(ret) == 2 assert len(ret) == 2
assert isinstance(ret[0], PropertiesOptionError) assert isinstance(ret[0], PropertiesOptionError)
@ -1003,89 +1039,89 @@ def test_mix_different_default():
mix = MixConfig(interface4, [mix]) mix = MixConfig(interface4, [mix])
mix.property.read_write() mix.property.read_write()
# #
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.6']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.6']}
newsubmix2 = mix.config('submix2') newsubmix2 = mix.config('submix2')
newsubmix1 = mix.config('submix2.submix1') newsubmix1 = mix.config('submix2.submix1')
newconf1 = mix.config('submix2.submix1.conf1') newconf1 = mix.config('submix2.submix1.conf1')
newconf2 = mix.config('submix2.submix1.conf2') newconf2 = mix.config('submix2.submix1.conf2')
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
# #
mix.option('ip_admin_eth0').value.set(['192.168.1.7']) mix.option('ip_admin_eth0').value.set(['192.168.1.7'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.7'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.7'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
# #
newsubmix2.option('ip_admin_eth0').value.set(['192.168.1.8']) newsubmix2.option('ip_admin_eth0').value.set(['192.168.1.8'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.8']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.8']}
# #
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
newsubmix1.option('ip_admin_eth0').value.set(['192.168.1.9']) newsubmix1.option('ip_admin_eth0').value.set(['192.168.1.9'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.8']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.8']}
# #
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
newconf2.option('ip_admin_eth0').value.set(['192.168.1.9']) newconf2.option('ip_admin_eth0').value.set(['192.168.1.9'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.8']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.8']}
# #
newconf1.option('ip_admin_eth0').value.set(['192.168.1.9']) newconf1.option('ip_admin_eth0').value.set(['192.168.1.9'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
mix.option('ip_admin_eth1').value.set(['192.168.1.10']) mix.option('ip_admin_eth1').value.set(['192.168.1.10'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
newsubmix2.option('ip_admin_eth1').value.set(['192.168.1.10']) newsubmix2.option('ip_admin_eth1').value.set(['192.168.1.10'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.10']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.10']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
newsubmix1.option('ip_admin_eth1').value.set(['192.168.1.11']) newsubmix1.option('ip_admin_eth1').value.set(['192.168.1.11'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
newconf2.option('ip_admin_eth1').value.set(['192.168.1.12']) newconf2.option('ip_admin_eth1').value.set(['192.168.1.12'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.12']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.12']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
newconf1.option('ip_admin_eth1').value.set(['192.168.1.13']) newconf1.option('ip_admin_eth1').value.set(['192.168.1.13'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(newsubmix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']} assert newsubmix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(newsubmix1.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert newsubmix1.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(newconf2.value.get()) == {'ip_admin_eth1': ['192.168.1.12']} assert newconf2.value.dict() == {'ip_admin_eth1': ['192.168.1.12']}
assert parse_od_get(newconf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert newconf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
def test_mix_different_default_reset(): def test_mix_different_default_reset():
@ -1119,25 +1155,25 @@ def test_mix_different_default_reset():
submix1.option('ip_admin_eth1').value.set(['192.168.1.11']) submix1.option('ip_admin_eth1').value.set(['192.168.1.11'])
conf2.option('ip_admin_eth1').value.set(['192.168.1.12']) conf2.option('ip_admin_eth1').value.set(['192.168.1.12'])
conf1.option('ip_admin_eth0').value.set(['192.168.1.9']) conf1.option('ip_admin_eth0').value.set(['192.168.1.9'])
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.7']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.7']}
assert parse_od_get(submix2.value.get()) == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']} assert submix2.value.dict() == {'ip_admin_eth0': ['192.168.1.8'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(submix1.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert submix1.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth1': ['192.168.1.12']} assert conf2.value.dict() == {'ip_admin_eth1': ['192.168.1.12']}
assert parse_od_get(conf1.value.get()) == {'ip_admin_eth0': ['192.168.1.9']} assert conf1.value.dict() == {'ip_admin_eth0': ['192.168.1.9']}
# #
mix.value.reset('ip_admin_eth0') mix.value.reset('ip_admin_eth0')
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.6']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.6']}
assert parse_od_get(submix2.value.get()) == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.10']} assert submix2.value.dict() == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.10']}
assert parse_od_get(submix1.value.get()) == {'ip_admin_eth1': ['192.168.1.11']} assert submix1.value.dict() == {'ip_admin_eth1': ['192.168.1.11']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth1': ['192.168.1.12']} assert conf2.value.dict() == {'ip_admin_eth1': ['192.168.1.12']}
assert parse_od_get(conf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert conf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
# #
mix.value.reset('ip_admin_eth1') mix.value.reset('ip_admin_eth1')
assert parse_od_get(mix.value.get()) == {'ip_admin_eth0': ['192.168.1.6']} assert mix.value.dict() == {'ip_admin_eth0': ['192.168.1.6']}
assert parse_od_get(submix2.value.get()) == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.5']} assert submix2.value.dict() == {'ip_admin_eth0': ['192.168.1.4'], 'ip_admin_eth1': ['192.168.1.5']}
assert parse_od_get(submix1.value.get()) == {'ip_admin_eth1': ['192.168.1.3']} assert submix1.value.dict() == {'ip_admin_eth1': ['192.168.1.3']}
assert parse_od_get(conf2.value.get()) == {'ip_admin_eth1': ['192.168.1.2']} assert conf2.value.dict() == {'ip_admin_eth1': ['192.168.1.2']}
assert parse_od_get(conf1.value.get()) == {'ip_admin_eth0': ['192.168.1.1']} assert conf1.value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
def test_mix_pop_config(): def test_mix_pop_config():
@ -1149,7 +1185,7 @@ def test_mix_pop_config():
# #
assert len(list(mix.config.list())) == 2 assert len(list(mix.config.list())) == 2
newconfig1 = mix.config('config1') newconfig1 = mix.config('config1')
assert parse_od_get(newconfig1.value.get()) == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert newconfig1.value.dict() == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
newconf1 = mix.config.remove('config1') newconf1 = mix.config.remove('config1')
try: try:
mix.config('config1') mix.config('config1')
@ -1157,7 +1193,7 @@ def test_mix_pop_config():
pass pass
else: else:
raise Exception('must raise') raise Exception('must raise')
assert parse_od_get(newconf1.value.get()) == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert newconf1.value.dict() == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
# #
assert len(list(mix.config.list())) == 1 assert len(list(mix.config.list())) == 1
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
@ -1173,11 +1209,11 @@ def test_mix_add_config():
# #
assert len(list(mix.config.list())) == 2 assert len(list(mix.config.list())) == 2
config = Config(od, name='new') config = Config(od, name='new')
assert parse_od_get(config.value.get()) == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert config.value.dict() == {'od1.i1': None, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
mix.config.add(config) mix.config.add(config)
# #
assert len(list(mix.config.list())) == 3 assert len(list(mix.config.list())) == 3
assert parse_od_get(config.value.get()) == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None} assert config.value.dict() == {'od1.i1': 2, 'od1.i2': 1, 'od1.i3': None, 'od1.i4': 2, 'od1.i5': [2], 'od1.i6': None}
# #
with pytest.raises(ConflictError): with pytest.raises(ConflictError):
mix.config.add(config) mix.config.add(config)

View file

@ -20,7 +20,7 @@ def a_func():
return None return None
def display_name(*args, with_quote=False): def display_name(*args):
return 'display_name' return 'display_name'
@ -34,6 +34,20 @@ def test_option_valid_name():
i = SymLinkOption("test1", i) i = SymLinkOption("test1", i)
def test_option_get_information():
description = "it's ok"
string = 'some informations'
i = IntOption('test', description)
with pytest.raises(ValueError):
i.impl_get_information('noinfo')
i.impl_set_information('info', string)
assert i.impl_get_information('info') == string
with pytest.raises(ValueError):
i.impl_get_information('noinfo')
assert i.impl_get_information('noinfo', 'default') == 'default'
assert i.impl_get_information('doc') == description
def test_option_get_information_config(): def test_option_get_information_config():
description = "it's ok" description = "it's ok"
string = 'some informations' string = 'some informations'
@ -41,16 +55,21 @@ def test_option_get_information_config():
od = OptionDescription('od', '', [i]) od = OptionDescription('od', '', [i])
cfg = Config(od) cfg = Config(od)
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('test').information.get('noinfo') i.impl_get_information('noinfo')
assert cfg.option('test').information.get('noinfo', 'default') == 'default' with pytest.raises(AttributeError):
assert cfg.option('test').information.get('doc') == description i.impl_set_information('info', string)
with pytest.raises(ValueError):
i.impl_get_information('noinfo')
assert i.impl_get_information('noinfo', 'default') == 'default'
assert i.impl_get_information('doc') == description
# assert not list_sessions() # assert not list_sessions()
def test_option_unknown(): def test_option_unknown():
description = "it's ok" description = "it's ok"
string = 'some informations' string = 'some informations'
i = IntOption('test', description, informations={'noinfo': 'optdefault'}) i = IntOption('test', description)
i.impl_set_information('noinfo', 'optdefault')
od = OptionDescription('od', '', [i]) od = OptionDescription('od', '', [i])
cfg = Config(od) cfg = Config(od)
# #
@ -74,7 +93,8 @@ def test_option_description():
def test_option_get_information_default(): def test_option_get_information_default():
description = "it's ok" description = "it's ok"
string = 'some informations' string = 'some informations'
i = IntOption('test', description, informations={'noinfo': 'optdefault'}) i = IntOption('test', description)
i.impl_set_information('noinfo', 'optdefault')
od = OptionDescription('od', '', [i]) od = OptionDescription('od', '', [i])
cfg = Config(od) cfg = Config(od)
# #
@ -88,30 +108,32 @@ def test_option_get_information_default():
def test_option_get_information_config2(): def test_option_get_information_config2():
description = "it's ok" description = "it's ok"
string = 'some informations' string = 'some informations'
i = IntOption('test', description, informations={'info': string}) i = IntOption('test', description)
i.impl_set_information('info', string)
od = OptionDescription('od', '', [i]) od = OptionDescription('od', '', [i])
cfg = Config(od) cfg = Config(od)
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('test').information.get('noinfo') i.impl_get_information('noinfo')
assert cfg.option('test').information.get('info') == string with pytest.raises(AttributeError):
i.impl_set_information('info', 'hello')
assert i.impl_get_information('info') == string
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('test').information.get('noinfo') i.impl_get_information('noinfo')
assert cfg.option('test').information.get('noinfo', 'default') == 'default' assert i.impl_get_information('noinfo', 'default') == 'default'
assert cfg.option('test').information.get('doc') == description assert i.impl_get_information('doc') == description
# assert not list_sessions() # assert not list_sessions()
def test_optiondescription_get_information(): def test_optiondescription_get_information():
description = "it's ok" description = "it's ok"
string = 'some informations' string = 'some informations'
o = OptionDescription('test', description, [], informations={'info': string}) o = OptionDescription('test', description, [])
od = OptionDescription('od', '', [o]) o.impl_set_information('info', string)
cfg = Config(od) assert o.impl_get_information('info') == string
assert cfg.option('test').information.get('info') == string
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('test').information.get('noinfo') o.impl_get_information('noinfo')
assert cfg.option('test').information.get('noinfo', 'default') == 'default' assert o.impl_get_information('noinfo', 'default') == 'default'
assert cfg.option('test').information.get('doc') == description assert o.impl_get_information('doc') == description
# assert not list_sessions() # assert not list_sessions()
@ -180,9 +202,24 @@ def test_optiondescription_list():
od2 = OptionDescription('od', '', [od1, od3]) od2 = OptionDescription('od', '', [od1, od3])
od4 = OptionDescription('od', '', [od2]) od4 = OptionDescription('od', '', [od2])
cfg = Config(od4) cfg = Config(od4)
assert len(list(cfg.option('od').list())) == 2 assert len(list(cfg.option('od').list('option'))) == 0
assert len(list(cfg.option('od.od').list())) == 1 assert len(list(cfg.option('od').list('optiondescription'))) == 2
assert len(list(cfg.option('od.od2').list())) == 1 assert len(list(cfg.option('od').list('optiondescription', group_type=groups.family))) == 1
assert len(list(cfg.option('od').list('optiondescription', group_type=groups.notfamily1))) == 1
assert len(list(cfg.option('od.od').list('option'))) == 1
assert len(list(cfg.option('od.od2').list('option'))) == 1
try:
list(cfg.option('od').list('unknown'))
except AssertionError:
pass
else:
raise Exception('must raise')
try:
list(cfg.option('od').list('option', group_type='toto'))
except AssertionError:
pass
else:
raise Exception('must raise')
# assert not list_sessions() # assert not list_sessions()
@ -196,7 +233,22 @@ def test_optiondescription_group():
od3.impl_set_group_type(groups.notfamily) od3.impl_set_group_type(groups.notfamily)
od2 = OptionDescription('od', '', [od1, od3]) od2 = OptionDescription('od', '', [od1, od3])
cfg = Config(od2) cfg = Config(od2)
assert len(list(cfg.list())) == 2 assert len(list(cfg.option.list('option'))) == 0
assert len(list(cfg.option.list('optiondescription'))) == 2
assert len(list(cfg.option.list('optiondescription', group_type=groups.family))) == 1
assert len(list(cfg.option.list('optiondescription', group_type=groups.notfamily))) == 1
try:
list(cfg.option.list('unknown'))
except AssertionError:
pass
else:
raise Exception('must raise')
try:
list(cfg.option.list('option', group_type='toto'))
except AssertionError:
pass
else:
raise Exception('must raise')
# assert not list_sessions() # assert not list_sessions()
@ -299,4 +351,4 @@ def test_option_display_name():
display_name=display_name, display_name=display_name,
) )
assert cfg.option('test1').name() == 'test1' assert cfg.option('test1').name() == 'test1'
assert cfg.option('test1').description() == 'display_name' assert cfg.option('test1').doc() == 'display_name'

View file

@ -13,7 +13,7 @@ from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation
from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError
from tiramisu.i18n import _ from tiramisu.i18n import _
from .config import config_type, get_config, parse_od_get, get_dependencies from .config import config_type, get_config
def return_val(): def return_val():
@ -289,12 +289,11 @@ def test_callback(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert isinstance(cfg.option('val1').value.get(uncalculated=True), Calculation)
assert cfg.option('val1').value.get() == 'val' assert cfg.option('val1').value.get() == 'val'
cfg.option('val1').value.set('new-val') cfg.option('val1').value.set('new-val')
assert cfg.option('val1').value.get() == 'new-val' assert cfg.option('val1').value.get() == 'new-val'
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
assert cfg.option('val1').value.defaultmulti() == None assert cfg.option('val1').defaultmulti() == None
cfg.option('val1').value.reset() cfg.option('val1').value.reset()
assert cfg.option('val1').value.get() == 'val' assert cfg.option('val1').value.get() == 'val'
# assert not list_sessions() # assert not list_sessions()
@ -416,7 +415,8 @@ def test_callback_information(config_type):
def test_callback_information2(config_type): def test_callback_information2(config_type):
val1 = StrOption('val1', "", Calculation(return_value, Params(ParamSelfInformation('information', 'no_value')))) val1 = StrOption('val1', "", Calculation(return_value, Params(ParamSelfInformation('information', 'no_value'))))
val2 = StrOption('val2', "", Calculation(return_value, Params(ParamSelfInformation('information'))), informations={'information': 'new_value'}) val2 = StrOption('val2', "", Calculation(return_value, Params(ParamSelfInformation('information'))))
val2.impl_set_information('information', 'new_value')
val3 = StrOption('val3', "", Calculation(return_value, Params(ParamSelfInformation('information')))) val3 = StrOption('val3', "", Calculation(return_value, Params(ParamSelfInformation('information'))))
od1 = OptionDescription('rootconfig', '', [val1, val2, val3]) od1 = OptionDescription('rootconfig', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
@ -432,8 +432,9 @@ def test_callback_information2(config_type):
def test_callback_information3(config_type): def test_callback_information3(config_type):
val1 = StrOption('val1', "", informations={'information': 'new_value'}) val1 = StrOption('val1', "")
val2 = StrOption('val2', "", Calculation(return_value, Params(ParamInformation('information', option=val1)))) val2 = StrOption('val2', "", Calculation(return_value, Params(ParamInformation('information', option=val1))))
val1.impl_set_information('information', 'new_value')
od1 = OptionDescription('rootconfig', '', [val1, val2]) od1 = OptionDescription('rootconfig', '', [val1, val2])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
@ -548,7 +549,7 @@ def test_callback_multi(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert cfg.option('val1').value.get() == ['val'] assert cfg.option('val1').value.get() == ['val']
cfg.option('val1').value.set(['new-val']) cfg.option('val1').value.set(['new-val'])
assert cfg.option('val1').value.defaultmulti() == None assert cfg.option('val1').defaultmulti() == None
assert cfg.option('val1').value.get() == ['new-val'] assert cfg.option('val1').value.get() == ['new-val']
cfg.option('val1').value.set(['new-val', 'new-val2']) cfg.option('val1').value.set(['new-val', 'new-val2'])
assert cfg.option('val1').value.get() == ['new-val', 'new-val2'] assert cfg.option('val1').value.get() == ['new-val', 'new-val2']
@ -693,7 +694,6 @@ def test_callback_leader_and_followers_leader(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert [(a.path(), a.index()) for a in cfg.option('val1').dependencies()] == [('val2.val2', None)]
assert cfg.option('val1').value.get() == ['val'] assert cfg.option('val1').value.get() == ['val']
assert cfg.option('val2.val2').value.get() == ['val'] assert cfg.option('val2.val2').value.get() == ['val']
# #
@ -784,29 +784,10 @@ def test_callback_leader_and_followers_leader2(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None)]
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None)] #, ('val2.val2', 0)]
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)] #, ('val2.val3', 0)]
#
cfg.option('val1.val1').value.set(['val']) cfg.option('val1.val1').value.set(['val'])
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None), ('val1.val3', 0)]
assert get_dependencies(cfg.option('val1.val2', 0)) == [('val1', None), ('val1.val3', 0)]
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None), ('val1.val4', 0)]
assert get_dependencies(cfg.option('val1.val3', 0)) == [('val1', None), ('val1.val4', 0)]
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)]
assert cfg.option('val1.val4', 0).value.get() == 'val2' assert cfg.option('val1.val4', 0).value.get() == 'val2'
assert cfg.option('val1.val3', 0).value.get() == 'val2' assert cfg.option('val1.val3', 0).value.get() == 'val2'
assert cfg.option('val1.val2', 0).value.get() == 'val2' assert cfg.option('val1.val2', 0).value.get() == 'val2'
#
cfg.option('val1.val1').value.set(['val1', 'val2'])
assert get_dependencies(cfg.option('val1.val2')) == [('val1', None), ('val1.val3', 0), ('val1.val3', 1)]
assert get_dependencies(cfg.option('val1.val2', 0)) == [('val1', None), ('val1.val3', 0)]
assert get_dependencies(cfg.option('val1.val2', 1)) == [('val1', None), ('val1.val3', 1)]
assert get_dependencies(cfg.option('val1.val3')) == [('val1', None), ('val1.val4', 0), ('val1.val4', 1)]
assert get_dependencies(cfg.option('val1.val3', 0)) == [('val1', None), ('val1.val4', 0)]
assert get_dependencies(cfg.option('val1.val3', 1)) == [('val1', None), ('val1.val4', 1)]
assert get_dependencies(cfg.option('val1.val4')) == [('val1', None)]
# assert not list_sessions() # assert not list_sessions()
@ -965,7 +946,7 @@ def test_consistency_leader_and_followers_leader_mandatory_transitive():
try: try:
cfg.option('val1.val2', 0).value.get() cfg.option('val1.val2', 0).value.get()
except PropertiesOptionError as error: except PropertiesOptionError as error:
assert str(error) == str(_('cannot access to {0} {1} at index "{2}" because has {3} {4}').format('option', '"val2"', 0, _('property'), '"disabled"')) assert str(error) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'val2', _('property'), '"disabled"'))
else: else:
raise Exception('must raises') raise Exception('must raises')
assert list(cfg.value.mandatory()) == [] assert list(cfg.value.mandatory()) == []
@ -983,7 +964,7 @@ def test_callback_leader_and_followers_leader_list(config_type):
assert cfg.option('val1.val1').value.get() == ['val', 'val'] assert cfg.option('val1.val1').value.get() == ['val', 'val']
assert cfg.option('val1.val2', 0).value.get() == None assert cfg.option('val1.val2', 0).value.get() == None
assert cfg.option('val1.val2', 1).value.get() == None assert cfg.option('val1.val2', 1).value.get() == None
default_multi = cfg.option('val1.val1').value.defaultmulti() default_multi = cfg.option('val1.val1').defaultmulti()
cfg.option('val1.val1').value.set(['val', 'val', default_multi]) cfg.option('val1.val1').value.set(['val', 'val', default_multi])
assert cfg.option('val1.val1').value.get() == ['val', 'val', None] assert cfg.option('val1.val1').value.get() == ['val', 'val', None]
assert cfg.option('val1.val2', 0).value.get() == None assert cfg.option('val1.val2', 0).value.get() == None
@ -1519,28 +1500,9 @@ def test_leadership_callback_description(config_type):
cfg.option('od.st.st1.st2', 0).value.set('yes') cfg.option('od.st.st1.st2', 0).value.set('yes')
assert cfg.option('od.st.st1.st1').owner.get() == owner assert cfg.option('od.st.st1.st1').owner.get() == owner
assert cfg.option('od.st.st1.st2', 0).owner.get() == owner assert cfg.option('od.st.st1.st2', 0).owner.get() == owner
assert get_dependencies(cfg.option('od.st.st1.st2')) == [('od.st.st1', None)]
# assert not list_sessions() # assert not list_sessions()
def test_leadership_callback_outside(config_type):
st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True, default_multi='val2')
stm = Leadership('st1', '', [st1, st2])
st3 = StrOption('st3', "", Calculation(return_value, Params(ParamOption(st2))), multi=True)
st = OptionDescription('st', '', [stm, st3])
od = OptionDescription('od', '', [st])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
cfg = get_config(cfg, config_type)
owner = cfg.owner.get()
cfg.option('od.st.st1.st1').value.set(['yes'])
assert parse_od_get(cfg.value.get()) == {'od.st.st1.st1': [{'od.st.st1.st1': 'yes', 'od.st.st1.st2': 'val2'}], 'od.st.st3': ['val2']}
assert get_dependencies(cfg.option('od.st.st1.st2')) == [('od.st.st1', None), ('od.st.st3', None,)]
## assert not list_sessions()
def test_callback_raise(): def test_callback_raise():
opt1 = BoolOption('opt1', 'Option 1', Calculation(return_raise)) opt1 = BoolOption('opt1', 'Option 1', Calculation(return_raise))
opt2 = BoolOption('opt2', 'Option 2', Calculation(return_valueerror)) opt2 = BoolOption('opt2', 'Option 2', Calculation(return_valueerror))
@ -1566,7 +1528,7 @@ def test_calc_value_simple(config_type):
od1 = OptionDescription('root', '', [val1, val2]) od1 = OptionDescription('root', '', [val1, val2])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val1'} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1'}
# assert not list_sessions() # assert not list_sessions()
@ -1577,7 +1539,7 @@ def test_calc_value_multi(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3]) od1 = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val2', 'val3': ['val1', 'val2']} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': ['val1', 'val2']}
# assert not list_sessions() # assert not list_sessions()
@ -1587,9 +1549,9 @@ def test_calc_value_disabled():
od1 = OptionDescription('root', '', [val1, val2]) od1 = OptionDescription('root', '', [val1, val2])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val1'} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1'}
cfg.option('val1').property.add('disabled') cfg.option('val1').property.add('disabled')
assert parse_od_get(cfg.value.get()) == {'val2': 'default_value'} assert cfg.value.dict() == {'val2': 'default_value'}
# assert not list_sessions() # assert not list_sessions()
@ -1604,9 +1566,9 @@ def test_calc_value_condition(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'boolean': True, 'val1': 'val1', 'val2': 'val1'} assert cfg.value.dict() == {'boolean': True, 'val1': 'val1', 'val2': 'val1'}
cfg.option('boolean').value.set(False) cfg.option('boolean').value.set(False)
assert parse_od_get(cfg.value.get()) == {'boolean': False, 'val1': 'val1', 'val2': 'default_value'} assert cfg.value.dict() == {'boolean': False, 'val1': 'val1', 'val2': 'default_value'}
# assert not list_sessions() # assert not list_sessions()
@ -1617,7 +1579,7 @@ def test_calc_value_allow_none(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3]) od1 = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': None, 'val3': ['val1', None]} assert cfg.value.dict() == {'val1': 'val1', 'val2': None, 'val3': ['val1', None]}
# assert not list_sessions() # assert not list_sessions()
@ -1628,7 +1590,7 @@ def test_calc_value_remove_duplicate(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3]) od1 = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val1', 'val3': ['val1']} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val1', 'val3': ['val1']}
# assert not list_sessions() # assert not list_sessions()
@ -1639,7 +1601,7 @@ def test_calc_value_remove_duplicate2(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3]) od1 = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': ['val1', 'val1'], 'val2': ['val1', 'val1'], 'val3': ['val1-val1']} assert cfg.value.dict() == {'val1': ['val1', 'val1'], 'val2': ['val1', 'val1'], 'val3': ['val1-val1']}
# assert not list_sessions() # assert not list_sessions()
@ -1651,9 +1613,9 @@ def test_calc_value_join(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3, val4]) od1 = OptionDescription('root', '', [val1, val2, val3, val4])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val2', 'val3': None, 'val4': None} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': None, 'val4': None}
cfg.option('val3').value.set('val3') cfg.option('val3').value.set('val3')
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'}
# assert not list_sessions() # assert not list_sessions()
@ -1663,9 +1625,9 @@ def test_calc_value_join_multi(config_type):
od1 = OptionDescription('root', '', [val1, val4]) od1 = OptionDescription('root', '', [val1, val4])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': [], 'val4': []} assert cfg.value.dict() == {'val1': [], 'val4': []}
cfg.option('val1').value.set(['val1']) cfg.option('val1').value.set(['val1'])
assert parse_od_get(cfg.value.get()) == {'val1': ['val1'], 'val4': ['val1']} assert cfg.value.dict() == {'val1': ['val1'], 'val4': ['val1']}
# assert not list_sessions() # assert not list_sessions()
@ -1677,9 +1639,9 @@ def test_calc_value_join_multi_value(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3, val4]) od1 = OptionDescription('root', '', [val1, val2, val3, val4])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': ['val1'], 'val2': ['val2'], 'val3': [None], 'val4': []} assert cfg.value.dict() == {'val1': ['val1'], 'val2': ['val2'], 'val3': [None], 'val4': []}
cfg.option('val3').value.set(['val3']) cfg.option('val3').value.set(['val3'])
assert parse_od_get(cfg.value.get()) == {'val1': ['val1'], 'val2': ['val2'], 'val3': ['val3'], 'val4': ['val1.val2.val3']} assert cfg.value.dict() == {'val1': ['val1'], 'val2': ['val2'], 'val3': ['val3'], 'val4': ['val1.val2.val3']}
# assert not list_sessions() # assert not list_sessions()
@ -1691,9 +1653,9 @@ def test_calc_value_min():
od1 = OptionDescription('root', '', [val1, val2, val3, val4]) od1 = OptionDescription('root', '', [val1, val2, val3, val4])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val3': 'val3', 'val4': 'val1.val2.val3'}
cfg.option('val3').property.add('disabled') cfg.option('val3').property.add('disabled')
assert parse_od_get(cfg.value.get()) == {'val1': 'val1', 'val2': 'val2', 'val4': ''} assert cfg.value.dict() == {'val1': 'val1', 'val2': 'val2', 'val4': ''}
# assert not list_sessions() # assert not list_sessions()
@ -1704,7 +1666,7 @@ def test_calc_value_add(config_type):
od1 = OptionDescription('root', '', [val1, val2, val3]) od1 = OptionDescription('root', '', [val1, val2, val3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'val1': 1, 'val2': 2, 'val3': 3} assert cfg.value.dict() == {'val1': 1, 'val2': 2, 'val3': 3}
# assert not list_sessions() # assert not list_sessions()
@ -1729,38 +1691,3 @@ def test_calc_dependencies(config_type):
def test_callback__kwargs_wrong(config_type): def test_callback__kwargs_wrong(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
Params(kwargs='string') Params(kwargs='string')
def test_callback_information_parent(config_type):
information = ParamInformation('information')
val1 = StrOption('val1', "", Calculation(return_value, Params(information)))
od2 = OptionDescription('od', '', [val1], informations={'information': 'new_value'})
information.set_option(od2)
od1 = OptionDescription('rootconfig', '', [od2])
cfg = Config(od1)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
assert cfg.option('od.val1').value.get() == 'new_value'
cfg.option('od').information.set('information', 'new_value2')
assert cfg.option('od.val1').value.get() == 'new_value2'
def test_callback_information_redefined(config_type):
val1 = StrOption('val1', "")
information = ParamInformation('information', option=val1)
val2 = StrOption('val2', "", Calculation(return_value, Params(information)))
od2 = OptionDescription('od', '', [val1, val2], informations={'information': 'new_value'})
with pytest.raises(ConfigError):
information.set_option(od2)
def test_callback_information_redefined_after(config_type):
information = ParamInformation('information')
val1 = StrOption('val1', "", Calculation(return_value, Params(information)))
od2 = OptionDescription('od', '', [val1], informations={'information': 'new_value'})
od1 = OptionDescription('rootconfig', '', [od2])
cfg = Config(od1)
cfg.property.read_write()
cfg = get_config(cfg, config_type)
with pytest.raises(ConfigError):
information.set_option(od2)

View file

@ -154,8 +154,8 @@ def test_force_default_on_freeze_multi():
# with pytest.raises(ConfigError): # with pytest.raises(ConfigError):
# Config(od1) # Config(od1)
# assert not list_sessions() # assert not list_sessions()
#
#
#def test_force_metaconfig_on_freeze_leader(): #def test_force_metaconfig_on_freeze_leader():
# dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',)) # dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',))
# dummy2 = BoolOption('dummy2', 'Test string option', multi=True) # dummy2 = BoolOption('dummy2', 'Test string option', multi=True)
@ -166,6 +166,28 @@ def test_force_default_on_freeze_multi():
# assert not list_sessions() # assert not list_sessions()
def test_force_default_on_freeze_leader_frozen():
dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze', 'frozen'))
dummy2 = BoolOption('dummy2', 'Test string option', multi=True)
descr = Leadership("dummy1", "", [dummy1, dummy2])
od1 = OptionDescription("root", "", [descr])
cfg = Config(od1)
with pytest.raises(LeadershipError):
cfg.option('dummy1.dummy1').property.remove('frozen')
# assert not list_sessions()
def test_force_metaconfig_on_freeze_leader_frozen():
dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze', 'frozen'))
dummy2 = BoolOption('dummy2', 'Test string option', multi=True)
descr = Leadership("dummy1", "", [dummy1, dummy2])
od1 = OptionDescription("root", "", [descr])
cfg = Config(od1)
with pytest.raises(LeadershipError):
cfg.option('dummy1.dummy1').property.remove('frozen')
# assert not list_sessions()
def test_force_default_on_freeze_follower(config_type): def test_force_default_on_freeze_follower(config_type):
dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('notunique',)) dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('notunique',))
dummy2 = BoolOption('dummy2', 'Test string option', multi=True, properties=('force_default_on_freeze',)) dummy2 = BoolOption('dummy2', 'Test string option', multi=True, properties=('force_default_on_freeze',))

View file

@ -64,16 +64,15 @@ def test_hidden_owner():
od1 = OptionDescription('tiramisu', '', [gcdummy]) od1 = OptionDescription('tiramisu', '', [gcdummy])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
with pytest.raises(PropertiesOptionError): #with pytest.raises(PropertiesOptionError):
cfg.option('dummy').value.get() # cfg.forcepermissive.option('dummy').owner.get()
with pytest.raises(PropertiesOptionError): #with pytest.raises(PropertiesOptionError):
cfg.option('dummy').owner.isdefault() # cfg.option('dummy').owner.isdefault()
#with pytest.raises(PropertiesOptionError):
# cfg.forcepermissive.option('dummy').owner.isdefault()
cfg.permissive.add('hidden') cfg.permissive.add('hidden')
cfg.forcepermissive.option('dummy').value.get() cfg.forcepermissive.option('dummy').value.get()
cfg.forcepermissive.option('dummy').owner.isdefault() cfg.forcepermissive.option('dummy').owner.isdefault()
option = cfg.option("dummy")
option.forcepermissive.value.get()
option.forcepermissive.owner.isdefault()
# assert not list_sessions() # assert not list_sessions()

View file

@ -1,23 +0,0 @@
""" RegexpOption
"""
from .autopath import do_autopath
do_autopath()
import pytest
from tiramisu import RegexpOption, OptionDescription, Config
import re
class ColorOption(RegexpOption):
__slots__ = tuple()
_type = 'Color'
_regexp = re.compile(r"^#(?:[0-9a-f]{3}){1,2}$")
def test_regexp_option():
r = ColorOption('test', 'test')
od = OptionDescription('od', 'od', [r])
cfg = Config(od)
cfg.option('test').value.set('#ff0000')
with pytest.raises(ValueError):
cfg.option('test').value.set('not a color')

View file

@ -14,7 +14,7 @@ from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \
calc_value, calc_value_property_help, ParamInformation calc_value, calc_value_property_help, ParamInformation
from tiramisu.error import PropertiesOptionError from tiramisu.error import PropertiesOptionError
import warnings import warnings
from .config import config_type, get_config, parse_od_get from .config import config_type, get_config
def make_description(): def make_description():
@ -82,7 +82,7 @@ def test_mod_read_only_write():
'empty', 'empty',
} }
# #
# config.property.setdefault(frozenset(['cache'])) config.property.setdefault(frozenset(['cache']))
config.property.setdefault(type='read_only', when='append', properties=frozenset(['disabled'])) config.property.setdefault(type='read_only', when='append', properties=frozenset(['disabled']))
config.property.setdefault(type='read_only', when='remove', properties=frozenset(['hidden'])) config.property.setdefault(type='read_only', when='remove', properties=frozenset(['hidden']))
config.property.setdefault(type='read_write', when='append', properties=frozenset(['disabled', 'hidden'])) config.property.setdefault(type='read_write', when='append', properties=frozenset(['disabled', 'hidden']))
@ -94,7 +94,7 @@ def test_mod_read_only_write():
with pytest.raises(TypeError): with pytest.raises(TypeError):
config.property.setdefault(type='read_only', when='append', properties=['disabled']) config.property.setdefault(type='read_only', when='append', properties=['disabled'])
assert config.property.default() == {'warnings', 'validator', 'cache'} assert config.property.default() == {'cache'}
assert config.property.default('read_only', 'append') == {'disabled'} assert config.property.default('read_only', 'append') == {'disabled'}
assert config.property.default('read_only', 'remove') == {'hidden'} assert config.property.default('read_only', 'remove') == {'hidden'}
assert config.property.default('read_write', 'append') == {'disabled', assert config.property.default('read_write', 'append') == {'disabled',
@ -102,11 +102,11 @@ def test_mod_read_only_write():
assert config.property.default('read_write', 'remove') == set([]) assert config.property.default('read_write', 'remove') == set([])
# #
config.property.read_only() config.property.read_only()
assert config.property.get() == {'warnings', 'validator', 'cache', 'disabled'} assert config.property.get() == {'cache', 'disabled'}
config.property.read_write() config.property.read_write()
assert config.property.get() == {'warnings', 'validator', 'cache', 'disabled', 'hidden'} assert config.property.get() == {'cache', 'disabled', 'hidden'}
config.property.read_only() config.property.read_only()
assert config.property.get() == {'warnings', 'validator', 'cache', 'disabled'} assert config.property.get() == {'cache', 'disabled'}
# #
assert config2.property.default() == {'cache', 'validator', 'warnings'} assert config2.property.default() == {'cache', 'validator', 'warnings'}
assert config2.property.default('read_only', 'append') == {'frozen', assert config2.property.default('read_only', 'append') == {'frozen',
@ -138,36 +138,8 @@ def test_mod_read_only_write():
# assert not list_sessions() # assert not list_sessions()
def test_setting_tree(config_type):
s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="value", multi=True)
od4 = OptionDescription("option4", "", [s])
od3 = OptionDescription("option3", "", [od4])
od2 = OptionDescription("option2", "", [od3], properties=('hidden',))
od1 = OptionDescription("root", "", [od2])
cfg = Config(od1)
cfg = get_config(cfg, config_type)
cfg.property.read_write()
with pytest.raises(PropertiesOptionError):
cfg.option('option2.option3.option4.string').value.get()
def test_setting_tree_2(config_type):
s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="value", multi=True)
od4 = OptionDescription("option4", "", [s])
od3 = OptionDescription("option3", "", [od4])
od2 = OptionDescription("option2", "", [od3], properties=('hidden',))
od1 = OptionDescription("root", "", [od2])
cfg = Config(od1)
cfg = get_config(cfg, config_type)
cfg.property.read_write()
option = cfg.option('option2.option3.option4.string')
option.isoptiondescription()
with pytest.raises(PropertiesOptionError):
option.value.get()
def test_setitem(config_type): def test_setitem(config_type):
s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="value", multi=True) s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="prout", multi=True)
od1 = OptionDescription("options", "", [s]) od1 = OptionDescription("options", "", [s])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
@ -217,10 +189,10 @@ def test_property_get_unique_empty():
od1 = OptionDescription("options", "", [s, s2, s3, s4]) od1 = OptionDescription("options", "", [s, s2, s3, s4])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('string').property.get() == {"validator", 'empty', 'unique'} assert cfg.option('string').property.get() == {'empty', 'unique'}
assert cfg.option('string2').property.get() == {"validator", 'empty', 'notunique'} assert cfg.option('string2').property.get() == {'empty', 'notunique'}
assert cfg.option('string3').property.get() == {"validator", 'unique', 'notempty'} assert cfg.option('string3').property.get() == {'unique', 'notempty'}
assert cfg.option('string4').property.get() == {"validator", 'notunique', 'notempty'} assert cfg.option('string4').property.get() == {'notunique', 'notempty'}
# assert not list_sessions() # assert not list_sessions()
@ -235,7 +207,7 @@ def test_property_only_raises():
od1 = OptionDescription("options", "", [s, intoption, stroption]) od1 = OptionDescription("options", "", [s, intoption, stroption])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('str').property.get() == {'empty', 'unique', 'validator'} assert cfg.option('str').property.get() == {'empty', 'unique'}
assert cfg.option('str').property.get(only_raises=True) == set() assert cfg.option('str').property.get(only_raises=True) == set()
# assert not list_sessions() # assert not list_sessions()
@ -256,7 +228,7 @@ def test_default_with_multi():
def test_idontexist(): def test_idontexist():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.value.get() cfg.value.dict()
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
cfg.option('idontexist').value.get() cfg.option('idontexist').value.get()
# assert not list_sessions() # assert not list_sessions()
@ -393,7 +365,8 @@ def test_apply_requires_from_config():
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('opt.str').value.get() cfg.option('opt.str').value.get()
assert 'hidden' in cfg.forcepermissive.option('opt.str').property.get() assert 'hidden' in cfg.forcepermissive.option('opt.str').property.get()
assert 'hidden' not in cfg.forcepermissive.option('opt.str').property.get(only_raises=True) assert 'hidden' not in cfg.forcepermissive.option('opt.str').properties()
assert 'hidden' not in cfg.forcepermissive.option('opt.str').properties(only_raises=True)
# assert not list_sessions() # assert not list_sessions()
@ -413,7 +386,8 @@ def test_apply_requires_with_disabled():
cfg.option('int').value.set(1) cfg.option('int').value.set(1)
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('opt.str').value.get() cfg.option('opt.str').value.get()
assert 'disabled' not in cfg.unrestraint.option('opt.str').property.get(only_raises=True, apply_requires=False) assert 'disabled' not in cfg.unrestraint.option('opt.str').properties()
assert 'disabled' not in cfg.unrestraint.option('opt.str').properties(only_raises=True)
assert 'disabled' in cfg.unrestraint.option('opt.str').property.get() assert 'disabled' in cfg.unrestraint.option('opt.str').property.get()
# assert not list_sessions() # assert not list_sessions()
@ -529,10 +503,10 @@ def test_accepts_multiple_changes_from_option():
od1 = OptionDescription("options", "", [s]) od1 = OptionDescription("options", "", [s])
cfg = Config(od1) cfg = Config(od1)
cfg.option('string').value.set("egg") cfg.option('string').value.set("egg")
assert cfg.option('string').value.default() == "string" assert cfg.option('string').default() == "string"
assert cfg.option('string').value.get() == "egg" assert cfg.option('string').value.get() == "egg"
cfg.option('string').value.set('blah') cfg.option('string').value.set('blah')
assert cfg.option('string').value.default() == "string" assert cfg.option('string').default() == "string"
assert cfg.option('string').value.get() == "blah" assert cfg.option('string').value.get() == "blah"
cfg.option('string').value.set('bol') cfg.option('string').value.set('bol')
assert cfg.option('string').value.get() == 'bol' assert cfg.option('string').value.get() == 'bol'
@ -561,8 +535,12 @@ def test_allow_multiple_changes_from_config():
def test_access_by_get(): def test_access_by_get():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('wantref').value.get() is False with pytest.raises(AttributeError):
assert cfg.option('gc.dummy').value.get() is False list(cfg.option.find('idontexist'))
ret = cfg.option.find('wantref', first=True)
assert ret.value.get() is False
ret = cfg.option.find('dummy', first=True)
assert ret.value.get() is False
# assert not list_sessions() # assert not list_sessions()
@ -577,30 +555,31 @@ def test_access_by_get_whith_hide():
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
cfg.option('b1').value.get() ret = cfg.option.find('b1')
ret.value.get()
# assert not list_sessions() # assert not list_sessions()
def test_append_properties(): def test_append_properties():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'test'}
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg.option('gc.dummy').property.add('force_store_value') cfg.option('gc.dummy').property.add('force_store_value')
assert cfg.option('gc.dummy').property.get() == {'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'test'}
# assert not list_sessions() # assert not list_sessions()
def test_reset_properties(): def test_reset_properties():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
cfg.option('gc.dummy').property.add('frozen') cfg.option('gc.dummy').property.add('frozen')
assert cfg.option('gc.dummy').property.get() == {"validator", 'frozen'} assert cfg.option('gc.dummy').property.get() == {'frozen'}
cfg.option('gc.dummy').property.reset() cfg.option('gc.dummy').property.reset()
assert cfg.option('gc.dummy').property.get() == {"validator"} assert cfg.option('gc.dummy').property.get() == set()
# assert not list_sessions() # assert not list_sessions()
@ -609,7 +588,7 @@ def test_properties_cached():
od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])]) od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.option('sub.b1').property.get() == {'test', "validator"} assert cfg.option('sub.b1').property.get() == {'test'}
# assert not list_sessions() # assert not list_sessions()
@ -618,9 +597,9 @@ def test_append_properties_force_store_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {'force_store_value', "validator"} assert cfg.option('gc.dummy').property.get() == {'force_store_value'}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test', "validator"} assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test'}
# assert not list_sessions() # assert not list_sessions()
@ -633,7 +612,7 @@ def test_properties_get_add_reset():
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.get() == {'validator', 'warnings', 'cache', 'frozen'} assert cfg.property.get() == {'validator', 'warnings', 'cache', 'frozen'}
cfg.property.reset() cfg.property.reset()
assert cfg.property.get() == frozenset() assert cfg.property.get() == {'validator', 'warnings', 'cache'}
def test_reset_properties_force_store_value(): def test_reset_properties_force_store_value():
@ -641,195 +620,28 @@ def test_reset_properties_force_store_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.property.exportation() == {'properties': {None: {None: frozenset({'cache', assert cfg.property.exportation() == {}
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{ {None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}}
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.reset() cfg.property.reset()
assert cfg.property.exportation() == {'properties': {None: {}}, assert cfg.property.exportation() == {None: {}}
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.property.exportation() == { assert cfg.property.exportation() == {None: {}, 'gc.dummy': {None: set(('test', 'force_store_value'))}}
'properties': {None: {},
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.reset() cfg.property.reset()
assert cfg.property.exportation() == { assert cfg.property.exportation() == {None: {}, 'gc.dummy': {None: set(('test', 'force_store_value'))}}
'properties': {None: {},
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{ {None: {None: set(('frozen', 'validator', 'cache', 'warnings'))},
'properties': {None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: set(('test', 'force_store_value'))}}
'gc.dummy': {None: frozenset({'test'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.add('frozen') cfg.property.add('frozen')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{ {None: {None: set(('frozen', 'validator', 'cache', 'warnings'))},
'properties': {None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}}, 'gc.dummy': {None: set(('test', 'force_store_value'))}}
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.option('gc.dummy').property.add('test') cfg.option('gc.dummy').property.add('test')
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{ {None: {None: set(('frozen', 'validator', 'cache', 'warnings'))},
'properties': {None: {None: frozenset({'frozen'})}, 'gc.dummy': {None: frozenset({'test'})}}, 'gc.dummy': {None: set(('test', 'force_store_value'))}}
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
# assert not list_sessions() # assert not list_sessions()
@ -859,79 +671,10 @@ def test_set_modified_value():
gcgroup = OptionDescription('gc', '', [gcdummy]) gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup]) od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1) cfg = Config(od1)
assert cfg.property.exportation() == { assert cfg.property.exportation() == {}
'properties': {None: {None: frozenset({'cache', cfg.property.importation({None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}})
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
cfg.property.importation({
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
})
assert cfg.property.exportation() == \ assert cfg.property.exportation() == \
{ {None: {None: set(('frozen', 'cache', 'validator', 'warnings'))}}
'properties': {None: {None: frozenset({'cache',
'frozen',
'validator',
'warnings'})}},
'ro_append': frozenset({'disabled',
'empty',
'everything_frozen',
'force_store_value',
'frozen',
'mandatory',
'validator'}),
'ro_remove': frozenset({'hidden',
'permissive'}),
'rw_append': frozenset({'disabled',
'force_store_value',
'frozen',
'hidden',
'validator'}),
'rw_remove': frozenset({'empty',
'everything_frozen',
'mandatory',
'permissive'}),
}
# assert not list_sessions() # assert not list_sessions()
@ -947,47 +690,8 @@ def test_none_is_not_modified():
# assert not list_sessions() # assert not list_sessions()
def test_force_store_value_disabled_value():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.option('gc.dummy1').value.get() == 'str'
# do not export
assert cfg._config_bag.context.get_values()._values == {None: {None: [None, 'user']},'gc.dummy1': {None: ['str', 'forced']}}
def test_force_store_value_disabled_owner():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.option('gc.dummy1').owner.get() == owners.forced
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
def test_force_store_value_disabled_exportation():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
def test_pprint(): def test_pprint():
msg_error = _("cannot access to {0} {1} because has {2} {3}") msg_error = _("cannot access to {0} \"{1}\" because has {2} {3}")
msg_is_not = _('the value of "{0}" is not {1}') msg_is_not = _('the value of "{0}" is not {1}')
msg_is = _('the value of "{0}" is {1}') msg_is = _('the value of "{0}" is {1}')
properties = _('properties') properties = _('properties')
@ -1041,8 +745,8 @@ def test_pprint():
err = error err = error
list_disabled = '"disabled" (' + display_list([msg_is.format('Test int option', '"1"'), msg_is.format('string2', '"string"')], add_quote=False) + ')' list_disabled = '"disabled" (' + display_list([msg_is.format('Test int option', '"1"'), msg_is.format('string2', '"string"')], add_quote=False) + ')'
list_hidden = '"hidden" (' + msg_is_not.format('Test int option', display_list([2, 3, 4], separator='or', add_quote=True)) + ')' list_hidden = '"hidden" (' + msg_is_not.format('Test int option', display_list([2, 3, 4], 'or', add_quote=True)) + ')'
assert str(err) == _(msg_error.format('option', '"Test string option"', properties, display_list([list_disabled, list_hidden], add_quote=False))) assert str(err) == _(msg_error.format('option', 'Test string option', properties, display_list([list_disabled, list_hidden], add_quote=False)))
del err del err
err = None err = None
@ -1051,7 +755,7 @@ def test_pprint():
except PropertiesOptionError as error: except PropertiesOptionError as error:
err = error err = error
assert str(err) == msg_error.format('optiondescription', '"options"', prop, '"hidden" (' + msg_is.format('Test int option', '"1"') + ')') assert str(err) == msg_error.format('optiondescription', 'options', prop, '"hidden" (' + msg_is.format('Test int option', '"1"') + ')')
#err = None #err = None
#try: #try:
@ -1072,7 +776,7 @@ def test_pprint():
except Exception as error: except Exception as error:
err = error err = error
assert str(err) == msg_error.format('option', '"string"', properties, display_list(['disabled', 'hidden'], add_quote=True)) assert str(err) == msg_error.format('option', 'string', properties, display_list(['disabled', 'hidden'], add_quote=True))
del err del err
err = None err = None
@ -1081,13 +785,13 @@ def test_pprint():
except Exception as error: except Exception as error:
err = error err = error
assert str(err) == msg_error.format('option', '"string3"', prop, '"hidden"') assert str(err) == msg_error.format('option', 'string3', prop, '"hidden"')
del err del err
# assert not list_sessions() # assert not list_sessions()
def test_pprint_not_todict(): def test_pprint_not_todict():
msg_error = _("cannot access to {0} {1} because has {2} {3}") msg_error = _("cannot access to {0} \"{1}\" because has {2} {3}")
msg_is_not = _('the value of "{0}" is not {1}') msg_is_not = _('the value of "{0}" is not {1}')
msg_is = _('the value of "{0}" is {1}') msg_is = _('the value of "{0}" is {1}')
properties = _('properties') properties = _('properties')
@ -1142,7 +846,7 @@ def test_pprint_not_todict():
list_disabled = '"disabled"' list_disabled = '"disabled"'
list_hidden = '"hidden"' list_hidden = '"hidden"'
assert str(err) == _(msg_error.format('option', '"Test string option"', properties, display_list([list_disabled, list_hidden], add_quote=False))) assert str(err) == _(msg_error.format('option', 'Test string option', properties, display_list([list_disabled, list_hidden], add_quote=False)))
del err del err
err = None err = None
@ -1151,7 +855,7 @@ def test_pprint_not_todict():
except PropertiesOptionError as error: except PropertiesOptionError as error:
err = error err = error
assert str(err) == msg_error.format('optiondescription', '"options"', prop, '"hidden"') assert str(err) == msg_error.format('optiondescription', 'options', prop, '"hidden"')
err = None err = None
try: try:
@ -1159,7 +863,7 @@ def test_pprint_not_todict():
except Exception as error: except Exception as error:
err = error err = error
assert str(err) == msg_error.format('option', '"string"', properties, display_list(['disabled', 'hidden'], add_quote=True)) assert str(err) == msg_error.format('option', 'string', properties, display_list(['disabled', 'hidden'], add_quote=True))
del err del err
err = None err = None
@ -1168,7 +872,7 @@ def test_pprint_not_todict():
except Exception as error: except Exception as error:
err = error err = error
assert str(err) == msg_error.format('option', '"string3"', prop, '"hidden"') assert str(err) == msg_error.format('option', 'string3', prop, '"hidden"')
del err del err
# assert not list_sessions() # assert not list_sessions()
@ -1189,9 +893,7 @@ def test_settings_list_with_follower():
descr = Leadership("root", "", [leader, option, ip]) descr = Leadership("root", "", [leader, option, ip])
cfg = Config(OptionDescription('root', 'root', [descr])) cfg = Config(OptionDescription('root', 'root', [descr]))
cfg.property.read_write() cfg.property.read_write()
assert parse_od_get(cfg.option('root').value.get()) == {'root.leader': ['leader']} assert len(cfg.option('root').list('all')) == 2
assert len(cfg.option('root').list()) == 1
assert len(cfg.option('root').list(validate_properties=False)) == 3
def return_none(*args): def return_none(*args):
@ -1206,4 +908,4 @@ def test_settings_disable_set_information(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
cfg.information.set('key', 'val') cfg.information.set('key', 'val')
assert parse_od_get(cfg.value.get()) == {'leadership.opt1': [{'leadership.opt1': 'val', 'leadership.opt2': 'test'}]} assert cfg.value.dict() == {'leadership.opt1': [{'leadership.opt1': 'val', 'leadership.opt2': 'test'}]}

View file

@ -64,10 +64,7 @@ def test_is_hidden(config_type):
def test_group_is_hidden(config_type): def test_group_is_hidden(config_type):
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=(('hidden'),)) od1 = make_description()
floatoption = FloatOption('float', 'Test float option', default=2.3)
gcgroup = OptionDescription('gc', '', [gcdummy, floatoption])
od1 = OptionDescription('trs', '', [gcgroup])
cfg_ori = Config(od1) cfg_ori = Config(od1)
cfg_ori.property.read_write() cfg_ori.property.read_write()
cfg_ori.option('gc').property.add('hidden') cfg_ori.option('gc').property.add('hidden')
@ -76,14 +73,14 @@ def test_group_is_hidden(config_type):
cfg.option('gc.dummy').value.get() cfg.option('gc.dummy').value.get()
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
assert 'hidden' in cfg_ori.option('gc').property.get() assert 'hidden' in cfg_ori.forcepermissive.option('gc').property.get()
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('gc.float').value.get() cfg.option('gc.float').value.get()
# manually set the subconfigs to "show" # manually set the subconfigs to "show"
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.option('gc').property.remove('hidden') cfg_ori.forcepermissive.option('gc').property.remove('hidden')
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
assert not 'hidden' in cfg.option('gc').property.get() assert not 'hidden' in cfg.option('gc').property.get()
assert cfg.option('gc.float').value.get() == 2.3 assert cfg.option('gc.float').value.get() == 2.3
@ -158,38 +155,13 @@ def test_with_many_subgroups(config_type):
def test_password_option(config_type): def test_password_option(config_type):
o = PasswordOption('o', '') o = PasswordOption('o', '')
o1 = PasswordOption('o1', '', min_len=4) od1 = OptionDescription('d', '', [o])
o2 = PasswordOption('o2', '', max_len=4)
o3 = PasswordOption('o3', '', forbidden_char=['p'])
od1 = OptionDescription('d', '', [o, o1, o2, o3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
cfg.option('o').value.set('a_valid_password') cfg.option('o').value.set('a_valid_password')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('o').value.set(1) cfg.option('o').value.set(1)
#
assert cfg.option('o1').value.get() is None
with pytest.raises(ValueError):
cfg.option('o1').value.set("1")
with pytest.raises(ValueError):
cfg.option('o1').value.set("12")
with pytest.raises(ValueError):
cfg.option('o1').value.set("123")
cfg.option('o1').value.set("1234")
cfg.option('o1').value.set("12345")
#
assert cfg.option('o2').value.get() is None
with pytest.raises(ValueError):
cfg.option('o2').value.set("12345")
cfg.option('o2').value.set("1")
cfg.option('o2').value.set("12")
cfg.option('o2').value.set("123")
cfg.option('o2').value.set("1234")
#
with pytest.raises(ValueError):
cfg.option('o3').value.set("password")
cfg.option('o3').value.set("assword")
# assert not list_sessions() # assert not list_sessions()

View file

@ -6,7 +6,7 @@ import pytest
from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \ from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \
IntOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, \ IntOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, \
ParamSelfOption, ParamIndex, ParamInformation, ParamSelfInformation, Calculation, \ ParamSelfOption, ParamIndex, ParamInformation, ParamSelfInformation, ParamSelfOption, Calculation, \
valid_ip_netmask, valid_network_netmask, \ valid_ip_netmask, valid_network_netmask, \
valid_in_network, valid_broadcast, valid_not_equal valid_in_network, valid_broadcast, valid_not_equal
from tiramisu.setting import groups from tiramisu.setting import groups
@ -34,7 +34,7 @@ def return_val(value, param=None):
def return_if_val(value): def return_if_val(value):
if value not in ['val', 'val_not_raise']: if value != 'val':
raise ValueError('test error') raise ValueError('test error')
@ -121,34 +121,6 @@ def test_validator(config_type):
# assert not list_sessions() # assert not list_sessions()
def test_validator_no_validation(config_type):
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()))], default='val', properties=frozenset(['novalidator']))
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))], properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
assert cfg.option('opt1').value.get() == 'val'
assert cfg.option('opt2').value.valid() is True
cfg.option('opt2').value.set('val')
def test_validator_no_validation2(config_type):
opt1 = StrOption('opt1', '', properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
cfg.option('opt1').value.set(1)
assert cfg.option('opt1').value.get() == 1
def test_validator_no_validation3(config_type):
opt1 = StrOption('opt1', '', 1, properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
assert cfg.option('opt1').value.get() == 1
def test_validator_not_valid(config_type): def test_validator_not_valid(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val') StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val')
@ -341,13 +313,6 @@ def test_validator_multi(config_type):
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.option('opt1').value.set(['val', 'val1']) cfg.option('opt1').value.set(['val', 'val1'])
assert len(w) == 1 assert len(w) == 1
with warnings.catch_warnings(record=True) as w:
cfg.option('opt1').value.set(['val1', 'val2'])
assert len(w) == 2
with warnings.catch_warnings(record=True) as w:
# same value twice
cfg.option('opt1').value.set(['val', 'val', 'val_not_raise'])
assert len(w) == 2
# assert not list_sessions() # assert not list_sessions()
@ -369,7 +334,7 @@ def test_validator_warning(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt2 assert w[0].message.opt() == opt2
assert str(w[0].message) == msg_err.format('val', _(opt2.get_type()), 'opt2') + ', ' + 'test error return_false' assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.nowarnings.option('opt2').value.set('val') cfg.nowarnings.option('opt2').value.set('val')
@ -387,7 +352,7 @@ def test_validator_warning(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt3 assert w[0].message.opt() == opt3
assert str(w[0].message) == msg_err.format('val1', _(opt3.get_type()), 'opt3') + ', ' + 'test error' assert str(w[0].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -400,9 +365,9 @@ def test_validator_warning(config_type):
assert len(w) == 2 assert len(w) == 2
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == opt2 assert w[0].message.opt() == opt2
assert str(w[0].message) == msg_err.format('val', _(opt2.get_type()), 'opt2') + ', ' + 'test error return_false' assert str(w[0].message) == msg_err.format('val', opt2.get_type(), 'opt2') + ', ' + 'test error return_false'
assert w[1].message.opt() == opt3 assert w[1].message.opt() == opt3
assert str(w[1].message) == msg_err.format('val1', _(opt3.get_type()), 'opt3') + ', ' + 'test error' assert str(w[1].message) == msg_err.format('val1', opt3.get_type(), 'opt3') + ', ' + 'test error'
# assert not list_sessions() # assert not list_sessions()
@ -471,13 +436,13 @@ def test_validator_warning_leadership(config_type):
assert len(w) == 1 assert len(w) == 1
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == netmask_admin_eth0 assert w[0].message.opt() == netmask_admin_eth0
assert str(w[0].message) == msg_err.format('val1', _(netmask_admin_eth0.get_type()), display_name_netmask) + ', test error' assert str(w[0].message) == msg_err.format('val1', netmask_admin_eth0.get_type(), display_name_netmask) + ', test error'
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w and w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 2 assert len(w) == 2
# #
@ -485,7 +450,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# #
@ -493,7 +458,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# #
@ -502,7 +467,7 @@ def test_validator_warning_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val'])
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
assert w[0].message.opt() == ip_admin_eth0 assert w[0].message.opt() == ip_admin_eth0
assert str(w[0].message) == msg_err.format('val', _(ip_admin_eth0.get_type()), display_name_ip) + ', test error return_false' assert str(w[0].message) == msg_err.format('val', ip_admin_eth0.get_type(), display_name_ip) + ', test error return_false'
else: else:
assert len(w) == 3 assert len(w) == 3
# assert not list_sessions() # assert not list_sessions()
@ -1000,7 +965,7 @@ def test_validator_broadcast_default_1():
od1 = OptionDescription('a', '', [a, b, c]) od1 = OptionDescription('a', '', [a, b, c])
cfg = Config(od1) cfg = Config(od1)
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -1010,7 +975,7 @@ def test_validator_broadcast_default_2():
d = BroadcastOption('d', '', '192.168.1.127', validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))]) d = BroadcastOption('d', '', '192.168.1.127', validators=[Calculation(valid_broadcast, Params((ParamOption(a), ParamOption(b), ParamSelfOption())))])
od1 = OptionDescription('a', '', [a, b, d]) od1 = OptionDescription('a', '', [a, b, d])
cfg = Config(od1) cfg = Config(od1)
assert cfg.value.get() assert cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -1036,7 +1001,7 @@ def test_validator_network_netmask_mandatory(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_only() cfg.property.read_only()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -1081,12 +1046,12 @@ def test_validator_error_prefix():
try: try:
cfg.option('b').value.set(1) cfg.option('b').value.set(1)
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('1', _('integer'), 'b') + ', ' + _('value is identical to {0}').format('"a"') assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('1', _('integer'), 'b') + ', ' + _('value is identical to {}').format('"a"')
try: try:
cfg.option('b').value.set(1) cfg.option('b').value.set(1)
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('value is identical to {0}').format('"a"') assert str(err) == _('value is identical to {}').format('"a"')
# assert not list_sessions() # assert not list_sessions()
@ -1144,7 +1109,7 @@ def test_validator_not_equal_leadership(config_type):
cfg.option('a.b', 0).value.set(2) cfg.option('a.b', 0).value.set(2)
cfg.option('a.a').value.reset() cfg.option('a.a').value.reset()
cfg.option('a.a').value.set([1]) cfg.option('a.a').value.set([1])
cfg.value.get() cfg.value.dict()
# assert not list_sessions() # assert not list_sessions()
@ -1295,24 +1260,18 @@ def test_consistency_not_equal_has_dependency():
def test_validator_information(config_type): def test_validator_information(config_type):
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfInformation('key'), ParamValue('yes'))))], default='val') opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params((ParamSelfInformation('key'), ParamValue('yes'))))], default='val')
opt2 = StrOption('opt2', '', validators=[Calculation(return_true, Params((ParamInformation('key'), ParamValue('yes'))))], default='val') opt2 = StrOption('opt2', '', validators=[Calculation(return_true, Params((ParamInformation('key'), ParamValue('yes'))))], default='val')
opt3 = StrOption('opt3', '', validators=[Calculation(return_true, Params((ParamInformation('key', option=opt1), ParamValue('yes'))))], default='val') od1 = OptionDescription('root', '', [opt1, opt2])
od1 = OptionDescription('root', '', [opt1, opt2, opt3])
cfg = Config(od1) cfg = Config(od1)
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg.option('opt1').value.get() cfg.option('opt1').value.get()
with pytest.raises(ConfigError):
cfg.option('opt3').value.get()
cfg.option('opt1').information.set('key', 'val') cfg.option('opt1').information.set('key', 'val')
assert cfg.option('opt1').value.get() == 'val' assert cfg.option('opt1').value.get() == 'val'
assert cfg.option('opt3').value.get() == 'val'
cfg.option('opt1').information.set('key', 'val1') cfg.option('opt1').information.set('key', 'val1')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('opt1').value.get() cfg.option('opt1').value.get()
with pytest.raises(ValueError):
cfg.option('opt3').value.get()
# #
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg.option('opt2').value.get() assert cfg.option('opt2').value.get()
cfg.information.set('key', 'val') cfg.information.set('key', 'val')
assert cfg.option('opt2').value.get() == 'val' assert cfg.option('opt2').value.get() == 'val'
cfg.information.set('key', 'val1') cfg.information.set('key', 'val1')

View file

@ -22,16 +22,7 @@ def test_forcepermissive_and_unrestraint(config_type):
cfg_ori.property.read_write() cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg_ori.unrestraint.forcepermissive cfg_ori.unrestraint.forcepermissive.add('disabled')
def test_unrestraint_and_unrestraint(config_type):
od1 = make_description()
cfg_ori = Config(od1)
cfg_ori.property.read_write()
cfg_ori.property.read_write()
cfg = get_config(cfg_ori, config_type)
cfg_ori.unrestraint.unrestraint
def test_permissive(config_type): def test_permissive(config_type):
@ -48,9 +39,9 @@ def test_permissive(config_type):
assert set(props) == {'disabled'} assert set(props) == {'disabled'}
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.permissive.add('disabled') cfg_ori.unrestraint.permissive.add('disabled')
cfg_ori.permissive.remove('hidden') cfg_ori.unrestraint.permissive.remove('hidden')
assert cfg_ori.permissive.get() == frozenset(['disabled']) assert cfg_ori.unrestraint.permissive.get() == frozenset(['disabled'])
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
props = frozenset() props = frozenset()
try: try:
@ -90,8 +81,8 @@ def test_permissive_add(config_type):
assert set(props) == {'disabled'} assert set(props) == {'disabled'}
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.permissive.add('disabled') cfg_ori.unrestraint.permissive.add('disabled')
assert cfg_ori.permissive.get() == frozenset(['hidden', 'disabled']) assert cfg_ori.unrestraint.permissive.get() == frozenset(['hidden', 'disabled'])
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
props = frozenset() props = frozenset()
try: try:
@ -128,10 +119,10 @@ def test_permissive_pop():
except PropertiesOptionError as err: except PropertiesOptionError as err:
props = err.proptype props = err.proptype
assert set(props) == {'disabled'} assert set(props) == {'disabled'}
cfg.permissive.add('disabled') cfg.unrestraint.permissive.add('disabled')
assert cfg.permissive.get() == frozenset(['hidden', 'disabled']) assert cfg.unrestraint.permissive.get() == frozenset(['hidden', 'disabled'])
cfg.forcepermissive.option('u1').value.get() cfg.forcepermissive.option('u1').value.get()
cfg.permissive.remove('disabled') cfg.unrestraint.permissive.remove('disabled')
props = frozenset() props = frozenset()
try: try:
cfg.forcepermissive.option('u1').value.get() cfg.forcepermissive.option('u1').value.get()
@ -145,14 +136,14 @@ def test_permissive_reset():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert cfg.permissive.get() == frozenset(['hidden']) assert cfg.unrestraint.permissive.get() == frozenset(['hidden'])
# #
cfg.permissive.add('disabled') cfg.unrestraint.permissive.add('disabled')
cfg.permissive.remove('hidden') cfg.unrestraint.permissive.remove('hidden')
assert cfg.permissive.get() == frozenset(['disabled']) assert cfg.unrestraint.permissive.get() == frozenset(['disabled'])
# #
cfg.permissive.reset() cfg.unrestraint.permissive.reset()
assert cfg.permissive.get() == frozenset(['hidden']) assert cfg.unrestraint.permissive.get() == frozenset()
# assert not list_sessions() # assert not list_sessions()
@ -166,9 +157,9 @@ def test_permissive_mandatory():
except PropertiesOptionError as err: except PropertiesOptionError as err:
props = err.proptype props = err.proptype
assert frozenset(props) == frozenset(['disabled']) assert frozenset(props) == frozenset(['disabled'])
cfg.permissive.add('mandatory') cfg.unrestraint.permissive.add('mandatory')
cfg.permissive.add('disabled') cfg.unrestraint.permissive.add('disabled')
assert cfg.permissive.get() == frozenset(['hidden', 'mandatory', 'disabled']) assert cfg.unrestraint.permissive.get() == frozenset(['mandatory', 'disabled'])
cfg.property.add('permissive') cfg.property.add('permissive')
cfg.option('u1').value.get() cfg.option('u1').value.get()
cfg.property.remove('permissive') cfg.property.remove('permissive')
@ -184,10 +175,10 @@ def test_permissive_frozen():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.permissive.remove('hidden') cfg.unrestraint.permissive.remove('hidden')
cfg.permissive.add('frozen') cfg.unrestraint.permissive.add('frozen')
cfg.permissive.add('disabled') cfg.unrestraint.permissive.add('disabled')
assert cfg.permissive.get() == frozenset(['frozen', 'disabled']) assert cfg.unrestraint.permissive.get() == frozenset(['frozen', 'disabled'])
assert cfg.permissive.get() == frozenset(['frozen', 'disabled']) assert cfg.permissive.get() == frozenset(['frozen', 'disabled'])
try: try:
cfg.option('u1').value.set(1) cfg.option('u1').value.set(1)
@ -238,7 +229,7 @@ def test_permissive_option(config_type):
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.option('u1').permissive.add('disabled') cfg_ori.unrestraint.option('u1').permissive.set(frozenset(['disabled']))
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
props = frozenset() props = frozenset()
try: try:
@ -302,7 +293,7 @@ def test_permissive_option_cache():
props = err.proptype props = err.proptype
assert set(props) == {'disabled'} assert set(props) == {'disabled'}
cfg.option('u1').permissive.add('disabled') cfg.unrestraint.option('u1').permissive.set(frozenset(['disabled']))
props = frozenset() props = frozenset()
try: try:
cfg.option('u1').value.get() cfg.option('u1').value.get()
@ -351,9 +342,8 @@ def test_permissive_option_mandatory():
except PropertiesOptionError as err: except PropertiesOptionError as err:
props = err.proptype props = err.proptype
assert frozenset(props) == frozenset(['disabled']) assert frozenset(props) == frozenset(['disabled'])
cfg.option('u1').permissive.add('mandatory') cfg.unrestraint.option('u1').permissive.set(frozenset(['mandatory', 'disabled']))
cfg.option('u1').permissive.add('disabled') assert cfg.unrestraint.option('u1').permissive.get() == frozenset(['mandatory', 'disabled'])
assert cfg.option('u1').permissive.get() == frozenset(['mandatory', 'disabled'])
cfg.property.add('permissive') cfg.property.add('permissive')
cfg.option('u1').value.get() cfg.option('u1').value.get()
cfg.property.remove('permissive') cfg.property.remove('permissive')
@ -369,8 +359,7 @@ def test_permissive_option_frozen():
od1 = make_description() od1 = make_description()
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.option('u1').permissive.add('disabled') cfg.unrestraint.option('u1').permissive.set(frozenset(['frozen', 'disabled']))
cfg.option('u1').permissive.add('frozen')
cfg.option('u1').value.set(1) cfg.option('u1').value.set(1)
assert cfg.option('u1').value.get() == 1 assert cfg.option('u1').value.get() == 1
cfg.property.add('permissive') cfg.property.add('permissive')
@ -380,6 +369,15 @@ def test_permissive_option_frozen():
# assert not list_sessions() # assert not list_sessions()
def test_invalid_option_permissive():
od1 = make_description()
cfg = Config(od1)
cfg.property.read_write()
with pytest.raises(TypeError):
cfg.unrestraint.option('u1').permissive.set(['frozen', 'disabled'])
# assert not list_sessions()
def test_remove_option_permissive(config_type): def test_remove_option_permissive(config_type):
var1 = StrOption('var1', '', u'value', properties=('hidden',)) var1 = StrOption('var1', '', u'value', properties=('hidden',))
od1 = OptionDescription('od1', '', [var1]) od1 = OptionDescription('od1', '', [var1])
@ -391,13 +389,13 @@ def test_remove_option_permissive(config_type):
cfg.option('od1.var1').value.get() cfg.option('od1.var1').value.get()
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.forcepermissive.option('od1.var1').permissive.add('hidden') cfg_ori.forcepermissive.option('od1.var1').permissive.set(frozenset(['hidden']))
assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset(['hidden']) assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset(['hidden'])
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
assert cfg.option('od1.var1').value.get() == 'value' assert cfg.option('od1.var1').value.get() == 'value'
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.forcepermissive.option('od1.var1').permissive.reset() cfg_ori.forcepermissive.option('od1.var1').permissive.set(frozenset())
assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset() assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset()
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
@ -416,7 +414,7 @@ def test_reset_option_permissive(config_type):
cfg.option('od1.var1').value.get() cfg.option('od1.var1').value.get()
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.forcepermissive.option('od1.var1').permissive.add('hidden') cfg_ori.forcepermissive.option('od1.var1').permissive.set(frozenset(['hidden']))
assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset(['hidden']) assert cfg_ori.forcepermissive.option('od1.var1').permissive.get() == frozenset(['hidden'])
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
assert cfg.option('od1.var1').value.get() == 'value' assert cfg.option('od1.var1').value.get() == 'value'

View file

@ -13,7 +13,7 @@ from tiramisu import IPOption, OptionDescription, BoolOption, IntOption, StrOpti
calc_value_property_help calc_value_property_help
from tiramisu.error import PropertiesOptionError, ConfigError, display_list from tiramisu.error import PropertiesOptionError, ConfigError, display_list
import pytest import pytest
from .config import config_type, get_config, parse_od_get from .config import config_type, get_config
def test_properties(config_type): def test_properties(config_type):
@ -31,12 +31,12 @@ def test_properties(config_type):
assert frozenset(props) == frozenset(['disabled']) assert frozenset(props) == frozenset(['disabled'])
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.unrestraint.option('ip_address_service').permissive.add('disabled') cfg_ori.unrestraint.option('ip_address_service').property.remove('disabled')
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
cfg.option('ip_address_service').value.get() cfg.option('ip_address_service').value.get()
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.unrestraint.option('ip_address_service').permissive.remove('disabled') cfg_ori.unrestraint.option('ip_address_service').property.add('disabled')
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
props = [] props = []
try: try:
@ -47,8 +47,8 @@ def test_properties(config_type):
# pop twice # pop twice
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
cfg_ori.unrestraint.option('ip_address_service').permissive.add('disabled') cfg_ori.unrestraint.option('ip_address_service').property.remove('disabled')
cfg_ori.unrestraint.option('ip_address_service').permissive.remove('disabled') cfg_ori.unrestraint.option('ip_address_service').property.remove('disabled')
# assert not list_sessions() # assert not list_sessions()
@ -196,10 +196,10 @@ def test_requires_same_action(config_type):
submsg = '"disabled" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')' submsg = '"disabled" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')'
if config_type == 'tiramisu': if config_type == 'tiramisu':
submsg = '"new" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')' submsg = '"new" (' + _('the value of "{0}" is {1}').format('activate_service', '"False"') + ')'
submsg = '"disabled" (' + str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"activate_service_web"', _('property'), submsg)) + ')' submsg = '"disabled" (' + str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'activate_service_web', _('property'), submsg)) + ')'
assert str(err) == str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"ip_address_service_web"', _('property'), submsg)) assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
#access to cache #access to cache
assert str(err) == str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"ip_address_service_web"', _('property'), submsg)) assert str(err) == str(_('cannot access to {0} "{1}" because has {2} {3}').format('option', 'ip_address_service_web', _('property'), submsg))
else: else:
# FIXME # FIXME
assert str(err) == 'error' assert str(err) == 'error'
@ -426,9 +426,8 @@ def test_requires_transitive_unrestraint(config_type):
# #
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
cfg.send() cfg.send()
assert cfg_ori.option('activate_service_web').property.get() == {'disabled', "validator"} assert cfg_ori.unrestraint.option('activate_service_web').property.get() == {'disabled'}
# FIXME assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'} assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'}
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled', "validator"}
# assert not list_sessions() # assert not list_sessions()
@ -592,7 +591,7 @@ def test_requires_transitive_hidden_disabled_multiple(config_type):
cfg.option('activate_service').value.set(False) cfg.option('activate_service').value.set(False)
except ConfigError as err: except ConfigError as err:
req = err req = err
error_msg = str(_('unable to transform tiramisu object to dict: {}').format(_('cannot access to option {0} because required option {1} has {2} {3}').format('ip_address_service_web', '"activate_service_web"', _('property'), '"disabled"'))) error_msg = str(_('unable to transform tiramisu object to dict: {}').format(_('cannot access to option "{0}" because required option "{1}" has {2} {3}').format('ip_address_service_web', 'activate_service_web', _('property'), '"disabled"')))
else: else:
cfg.option('activate_service').value.set(False) cfg.option('activate_service').value.set(False)
# #
@ -608,26 +607,25 @@ def test_requires_transitive_hidden_disabled_multiple(config_type):
cfg.option('ip_address_service_web').value.get() cfg.option('ip_address_service_web').value.get()
except ConfigError as err: except ConfigError as err:
req = err req = err
error_msg = str(_('unable to carry out a calculation for {}, {}').format('"ip_address_service_web"', _('cannot access to {0} {1} because has {2} {3}').format('option', '"activate_service_web"', _('property'), display_list(['disabled'], add_quote=True)))) error_msg = str(_('unable to carry out a calculation for "{}", {}').format('ip_address_service_web', _('cannot access to {0} "{1}" because has {2} {3}').format('option', 'activate_service_web', _('property'), display_list(['disabled'], add_quote=True))))
assert req, "ip_address_service_web should raise ConfigError" assert req, "ip_address_service_web should raise ConfigError"
assert str(req) == error_msg assert str(req) == error_msg
del req del req
# #
cfg_ori.permissive.reset() cfg_ori.permissive.reset()
cfg_ori.permissive.remove('hidden')
if config_type == 'tiramisu-api': if config_type == 'tiramisu-api':
try: try:
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
except ConfigError as err: except ConfigError as err:
req = err req = err
error_msg = str(_('unable to transform tiramisu object to dict: {}').format(_('cannot access to option "{0}" because required option {1} has {2} {3}').format('ip_address_service_web', '"activate_service_web"', _('properties'), '"disabled" {} "hidden"'.format(_('and'))))) error_msg = str(_('unable to transform tiramisu object to dict: {}').format(_('cannot access to option "{0}" because required option "{1}" has {2} {3}').format('ip_address_service_web', 'activate_service_web', _('properties'), '"disabled" {} "hidden"'.format(_('and')))))
else: else:
cfg = get_config(cfg_ori, config_type) cfg = get_config(cfg_ori, config_type)
try: try:
cfg.option('ip_address_service_web').value.get() cfg.option('ip_address_service_web').value.get()
except ConfigError as err: except ConfigError as err:
req = err req = err
error_msg = str(_('unable to carry out a calculation for {}, {}').format('"ip_address_service_web"', _('cannot access to {0} {1} because has {2} {3}').format('option', '"activate_service_web"', _('properties'), display_list(['hidden', 'disabled'], add_quote=True)))) error_msg = str(_('unable to carry out a calculation for "{}", {}').format('ip_address_service_web', _('cannot access to {0} "{1}" because has {2} {3}').format('option', 'activate_service_web', _('properties'), display_list(['hidden', 'disabled'], add_quote=True))))
assert req, "ip_address_service_web should raise ConfigError" assert req, "ip_address_service_web should raise ConfigError"
assert str(req) == error_msg assert str(req) == error_msg
del req del req
@ -1108,19 +1106,19 @@ def test_leadership_requires(config_type):
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() is None assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() is None
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('255.255.255.255') cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('255.255.255.255')
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == '255.255.255.255' assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == '255.255.255.255'
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', 'ip_admin_eth0.netmask_admin_eth0': None}, assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', 'ip_admin_eth0.netmask_admin_eth0': None},
{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', 'ip_admin_eth0.netmask_admin_eth0': '255.255.255.255'}]} {'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', 'ip_admin_eth0.netmask_admin_eth0': '255.255.255.255'}]}
# #
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2', '192.168.1.1'])
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2',
'ip_admin_eth0.netmask_admin_eth0': None}, 'ip_admin_eth0.netmask_admin_eth0': None},
{'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]} {'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]}
# #
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('255.255.255.255') cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('255.255.255.255')
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2', assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2',
'ip_admin_eth0.netmask_admin_eth0': '255.255.255.255'}, 'ip_admin_eth0.netmask_admin_eth0': '255.255.255.255'},
{'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]} {'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]}
# assert not list_sessions() # assert not list_sessions()
@ -1160,7 +1158,7 @@ def test_leadership_requires_leader(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
assert parse_od_get(cfg.value.get()) == {'activate': False} assert cfg.value.dict() == {'activate': False}
# assert not list_sessions() # assert not list_sessions()
@ -1186,6 +1184,7 @@ def test_leadership_requires_leadership(config_type):
# #
cfg.option('activate').value.set(False) cfg.option('activate').value.set(False)
if config_type != 'tiramisu-api': if config_type != 'tiramisu-api':
# FIXME
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
@ -1201,7 +1200,7 @@ def test_leadership_requires_leadership(config_type):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
assert parse_od_get(cfg.value.get()) == {'activate': False} assert cfg.value.dict() == {'activate': False}
# assert not list_sessions() # assert not list_sessions()
@ -1239,7 +1238,7 @@ def test_leadership_requires_no_leader(config_type):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
with pytest.raises(PropertiesOptionError): with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get()
assert parse_od_get(cfg.value.get()) == {'activate': False, 'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2'}, {'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]} assert cfg.value.dict() == {'activate': False, 'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': '192.168.1.2'}, {'ip_admin_eth0.ip_admin_eth0': '192.168.1.1'}]}
# assert not list_sessions() # assert not list_sessions()
@ -1300,10 +1299,10 @@ def test_leadership_requires_complet(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
cfg.option('options.unicode.unicode').value.set(['test', 'trah']) cfg.option('options.unicode.unicode').value.set(['test', 'trah'])
cfg.option('options.unicode.unicode2', 0).value.set('test') cfg.option('options.unicode.unicode2', 0).value.set('test')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'test', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': None, 'options.unicode.unicode4': None}, {'options.unicode.unicode': 'trah', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': None} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'test', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': None, 'options.unicode.unicode4': None}, {'options.unicode.unicode': 'trah', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': None}
# #
cfg.option('options.unicodetoto').value.set('test') cfg.option('options.unicodetoto').value.set('test')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'test', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': None, 'options.unicode.unicode4': None, 'options.unicode.unicode5': None, 'options.unicode.unicode6': None, 'options.unicode.unicode7': None}, {'options.unicode.unicode': 'trah', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None, 'options.unicode.unicode5': None}], 'options.unicodetoto': 'test'} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'test', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': None, 'options.unicode.unicode4': None, 'options.unicode.unicode5': None, 'options.unicode.unicode6': None, 'options.unicode.unicode7': None}, {'options.unicode.unicode': 'trah', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None, 'options.unicode.unicode5': None}], 'options.unicodetoto': 'test'}
# assert not list_sessions() # assert not list_sessions()
@ -1340,24 +1339,24 @@ def test_leadership_requires_transitive1(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [], 'options.unicodetoto': None} assert cfg.value.dict() == {'options.unicode.unicode': [], 'options.unicodetoto': None}
# #
cfg.option('options.unicodetoto').value.set('test') cfg.option('options.unicodetoto').value.set('test')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [], 'options.unicodetoto': 'test'} assert cfg.value.dict() == {'options.unicode.unicode': [], 'options.unicodetoto': 'test'}
# #
cfg.option('options.unicode.unicode').value.set(['a', 'b', 'c']) cfg.option('options.unicode.unicode').value.set(['a', 'b', 'c'])
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'}
cfg.option('options.unicode.unicode2', 1).value.set('test') cfg.option('options.unicode.unicode2', 1).value.set('test')
cfg.option('options.unicode.unicode3', 1).value.set('test') cfg.option('options.unicode.unicode3', 1).value.set('test')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': 'test', 'options.unicode.unicode4': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'test', 'options.unicode.unicode3': 'test', 'options.unicode.unicode4': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'}
# #
cfg.option('options.unicode.unicode2', 1).value.set('rah') cfg.option('options.unicode.unicode2', 1).value.set('rah')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'rah'}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None, 'options.unicode.unicode2': 'rah'}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None, 'options.unicode.unicode2': None}], 'options.unicodetoto': 'test'}
# #
cfg.option('options.unicode.unicode2', 1).value.set('test') cfg.option('options.unicode.unicode2', 1).value.set('test')
cfg.option('options.unicodetoto').value.set('rah') cfg.option('options.unicodetoto').value.set('rah')
assert parse_od_get(cfg.value.get()) == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None}], 'options.unicodetoto': 'rah'} assert cfg.value.dict() == {'options.unicode.unicode': [{'options.unicode.unicode': 'a', 'options.unicode.unicode1': None}, {'options.unicode.unicode': 'b', 'options.unicode.unicode1': None}, {'options.unicode.unicode': 'c', 'options.unicode.unicode1': None}], 'options.unicodetoto': 'rah'}
# assert not list_sessions() # assert not list_sessions()

View file

@ -146,8 +146,8 @@ def test_slots_option_readonly():
with pytest.raises(AttributeError): with pytest.raises(AttributeError):
q._requires = 'q' q._requires = 'q'
# assert not list_sessions() # assert not list_sessions()
#
#
#def test_slots_description(): #def test_slots_description():
# # __slots__ for OptionDescription should be complete for __getattr__ # # __slots__ for OptionDescription should be complete for __getattr__
# slots = set() # slots = set()

View file

@ -8,7 +8,7 @@ import warnings
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import StrOption, IntOption, OptionDescription, submulti, Leadership, Config, \ from tiramisu import StrOption, IntOption, OptionDescription, submulti, Leadership, Config, \
MetaConfig, Params, ParamOption, Calculation MetaConfig, Params, ParamOption, Calculation
from tiramisu.error import LeadershipError, PropertiesOptionError, ValueOptionError from tiramisu.error import LeadershipError, PropertiesOptionError
def return_val(val=None): def return_val(val=None):
@ -115,18 +115,18 @@ def test_append_submulti():
# #
assert cfg.option('multi2').value.get() == [] assert cfg.option('multi2').value.get() == []
assert cfg.option('multi2').owner.get() == owners.default assert cfg.option('multi2').owner.get() == owners.default
cfg.option('multi2').value.set([cfg.option('multi2').value.defaultmulti()]) cfg.option('multi2').value.set([cfg.option('multi2').defaultmulti()])
assert cfg.option('multi2').owner.get() == owner assert cfg.option('multi2').owner.get() == owner
assert cfg.option('multi2').value.get() == [['yes']] assert cfg.option('multi2').value.get() == [['yes']]
cfg.option('multi2').value.set([cfg.option('multi2').value.defaultmulti(), ['no']]) cfg.option('multi2').value.set([cfg.option('multi2').defaultmulti(), ['no']])
assert cfg.option('multi2').value.get() == [['yes'], ['no']] assert cfg.option('multi2').value.get() == [['yes'], ['no']]
# #
assert cfg.option('multi3').value.get() == [['yes']] assert cfg.option('multi3').value.get() == [['yes']]
assert cfg.option('multi3').owner.get() == owners.default assert cfg.option('multi3').owner.get() == owners.default
cfg.option('multi3').value.set([cfg.option('multi2').value.defaultmulti(), []]) cfg.option('multi3').value.set([cfg.option('multi2').defaultmulti(), []])
assert cfg.option('multi3').owner.get() == owner assert cfg.option('multi3').owner.get() == owner
assert cfg.option('multi3').value.get() == [['yes'], []] assert cfg.option('multi3').value.get() == [['yes'], []]
cfg.option('multi3').value.set([cfg.option('multi2').value.defaultmulti(), [], ['no']]) cfg.option('multi3').value.set([cfg.option('multi2').defaultmulti(), [], ['no']])
assert cfg.option('multi3').value.get() == [['yes'], [], ['no']] assert cfg.option('multi3').value.get() == [['yes'], [], ['no']]
# assert not list_sessions() # assert not list_sessions()
@ -193,7 +193,7 @@ def test_callback_submulti_str():
owner = cfg.owner.get() owner = cfg.owner.get()
assert cfg.option('multi').owner.get() == owners.default assert cfg.option('multi').owner.get() == owners.default
assert cfg.option('multi').value.get() == [['val']] assert cfg.option('multi').value.get() == [['val']]
cfg.option('multi').value.set([['val'], cfg.option('multi').value.defaultmulti()]) cfg.option('multi').value.set([['val'], cfg.option('multi').defaultmulti()])
assert cfg.option('multi').owner.get() == owner assert cfg.option('multi').owner.get() == owner
assert cfg.option('multi').value.get() == [['val'], ['val']] assert cfg.option('multi').value.get() == [['val'], ['val']]
cfg.option('multi').value.reset() cfg.option('multi').value.reset()
@ -209,10 +209,10 @@ def test_callback_submulti_list():
owner = cfg.owner.get() owner = cfg.owner.get()
assert cfg.option('multi').value.get() == [['val', 'val']] assert cfg.option('multi').value.get() == [['val', 'val']]
assert cfg.option('multi').owner.get() == owners.default assert cfg.option('multi').owner.get() == owners.default
cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').value.defaultmulti()]) cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').defaultmulti()])
assert cfg.option('multi').owner.get() == owner assert cfg.option('multi').owner.get() == owner
assert cfg.option('multi').value.get() == [['val', 'val'], ['val', 'val']] assert cfg.option('multi').value.get() == [['val', 'val'], ['val', 'val']]
cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').value.defaultmulti(), cfg.option('multi').value.defaultmulti()]) cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').defaultmulti(), cfg.option('multi').defaultmulti()])
assert cfg.option('multi').value.get() == [['val', 'val'], ['val', 'val'], ['val', 'val']] assert cfg.option('multi').value.get() == [['val', 'val'], ['val', 'val'], ['val', 'val']]
cfg.option('multi').value.reset() cfg.option('multi').value.reset()
assert cfg.option('multi').owner.get() == owners.default assert cfg.option('multi').owner.get() == owners.default
@ -227,7 +227,7 @@ def test_callback_submulti_list_list():
owner = cfg.owner.get() owner = cfg.owner.get()
assert cfg.option('multi').value.get() == [['val', 'val']] assert cfg.option('multi').value.get() == [['val', 'val']]
assert cfg.option('multi').owner.get() == owners.default assert cfg.option('multi').owner.get() == owners.default
cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').value.defaultmulti()]) cfg.option('multi').value.set([['val', 'val'], cfg.option('multi').defaultmulti()])
assert cfg.option('multi').owner.get() == owner assert cfg.option('multi').owner.get() == owner
assert cfg.option('multi').value.get() == [['val', 'val'], []] assert cfg.option('multi').value.get() == [['val', 'val'], []]
cfg.option('multi').value.reset() cfg.option('multi').value.reset()
@ -317,8 +317,10 @@ def test_values_with_leader_and_followers_submulti_mandatory():
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
# #
cfg.property.read_write() cfg.property.read_write()
with pytest.raises(ValueOptionError): cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0", '', "255.255.255.0"])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(["255.255.255.0", "255.255.255.0"]) cfg.property.read_only()
with pytest.raises(PropertiesOptionError):
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()
# assert not list_sessions() # assert not list_sessions()
@ -380,6 +382,7 @@ def test_values_with_leader_and_followers_follower_submulti():
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0']) cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0'])
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.230.145']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.230.145'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0']) cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0', '255.255.255.0'])
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.reset() cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.reset()
cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0']) cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set(['255.255.255.0'])
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.230.145', '192.168.230.145']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.230.145', '192.168.230.145'])
@ -498,7 +501,7 @@ def test_callback_submulti():
def test_callback_submulti_follower(): def test_callback_submulti_follower():
multi = StrOption('multi', '', multi=True) multi = StrOption('multi', '', multi=True)
multi2 = StrOption('multi2', '', Calculation(return_list), multi=submulti, properties=('notunique',)) multi2 = StrOption('multi2', '', Calculation(return_list), multi=submulti)
od = Leadership('multi', '', [multi, multi2]) od = Leadership('multi', '', [multi, multi2])
od1 = OptionDescription('multi', '', [od]) od1 = OptionDescription('multi', '', [od])
cfg = Config(od1) cfg = Config(od1)
@ -530,7 +533,7 @@ def test_multi_submulti_meta():
od1 = OptionDescription('od', '', [multi]) od1 = OptionDescription('od', '', [multi])
cfg = Config(od1, name='cfg') cfg = Config(od1, name='cfg')
cfg.property.read_write() cfg.property.read_write()
cfg2 = Config(od1, name="cfg2") cfg2 = Config(od1)
cfg2.property.read_write() cfg2.property.read_write()
meta = MetaConfig([cfg, cfg2]) meta = MetaConfig([cfg, cfg2])
meta.property.read_write() meta.property.read_write()
@ -551,7 +554,7 @@ def test_multi_submulti_meta_no_cache():
od1 = OptionDescription('od', '', [multi]) od1 = OptionDescription('od', '', [multi])
cfg = Config(od1, name='cfg') cfg = Config(od1, name='cfg')
cfg.property.read_write() cfg.property.read_write()
cfg2 = Config(od1, name="cfg2") cfg2 = Config(od1)
cfg.property.read_write() cfg.property.read_write()
meta = MetaConfig([cfg, cfg2]) meta = MetaConfig([cfg, cfg2])
meta.property.read_write() meta.property.read_write()

View file

@ -2,7 +2,7 @@
import pytest import pytest
from .autopath import do_autopath from .autopath import do_autopath
do_autopath() do_autopath()
from .config import config_type, get_config, parse_od_get from .config import config_type, get_config
from tiramisu import BoolOption, StrOption, SymLinkOption, submulti, \ from tiramisu import BoolOption, StrOption, SymLinkOption, submulti, \
OptionDescription, Leadership, Config, Calculation, calc_value, Params, ParamOption, ParamValue OptionDescription, Leadership, Config, Calculation, calc_value, Params, ParamOption, ParamValue
@ -26,10 +26,8 @@ def test_symlink_option(config_type):
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert not cfg.option('s1.b').issymlinkoption() assert not cfg.option('s1.b').issymlinkoption()
assert cfg.option('c').issymlinkoption() assert cfg.option('c').issymlinkoption()
assert cfg.option('s1.b').type() == 'boolean' assert cfg.option('s1.b').type() == _('boolean')
assert cfg.option('c').type() == 'boolean' assert cfg.option('c').type() == _('boolean')
assert cfg.option('s1.b').type(only_self=True) == 'boolean'
assert cfg.option('c').type(only_self=True) == 'symlink'
assert cfg.option('s1.b').value.get() is False assert cfg.option('s1.b').value.get() is False
cfg.option("s1.b").value.set(True) cfg.option("s1.b").value.set(True)
cfg.option("s1.b").value.set(False) cfg.option("s1.b").value.set(False)
@ -56,13 +54,17 @@ def test_symlink_default(config_type):
assert not cfg.option('c').ismulti() assert not cfg.option('c').ismulti()
assert not cfg.option('s1.b').issubmulti() assert not cfg.option('s1.b').issubmulti()
assert not cfg.option('c').issubmulti() assert not cfg.option('c').issubmulti()
assert not cfg.option('s1.b').default()
assert not cfg.option('c').default()
assert not cfg.option('s1.b').value.default() assert not cfg.option('s1.b').value.default()
assert not cfg.option('c').value.default() assert not cfg.option('c').value.default()
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
assert not cfg.option('s1.b').value.defaultmulti() assert not cfg.option('s1.b').defaultmulti()
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
assert not cfg.option('c').value.defaultmulti() assert not cfg.option('c').defaultmulti()
cfg.option("s1.b").value.set(True) cfg.option("s1.b").value.set(True)
assert not cfg.option('s1.b').default()
assert not cfg.option('c').default()
assert not cfg.option('s1.b').value.default() assert not cfg.option('s1.b').value.default()
assert not cfg.option('c').value.default() assert not cfg.option('c').value.default()
## assert not list_sessions() ## assert not list_sessions()
@ -80,15 +82,19 @@ def test_symlink_default_multi(config_type):
assert cfg.option('c').ismulti() assert cfg.option('c').ismulti()
assert not cfg.option('s1.b').issubmulti() assert not cfg.option('s1.b').issubmulti()
assert not cfg.option('c').issubmulti() assert not cfg.option('c').issubmulti()
assert cfg.option('s1.b').default() == [False]
assert cfg.option('c').default() == [False]
assert cfg.option('s1.b').value.default() == [False] assert cfg.option('s1.b').value.default() == [False]
assert cfg.option('c').value.default() == [False] assert cfg.option('c').value.default() == [False]
assert cfg.option('s1.b').value.defaultmulti() assert cfg.option('s1.b').defaultmulti()
assert cfg.option('c').value.defaultmulti() assert cfg.option('c').defaultmulti()
cfg.option("s1.b").value.set([True]) cfg.option("s1.b").value.set([True])
assert cfg.option('s1.b').default() == [False]
assert cfg.option('c').default() == [False]
assert cfg.option('s1.b').value.default() == [False] assert cfg.option('s1.b').value.default() == [False]
assert cfg.option('c').value.default() == [False] assert cfg.option('c').value.default() == [False]
assert cfg.option('s1.b').value.defaultmulti() assert cfg.option('s1.b').defaultmulti()
assert cfg.option('c').value.defaultmulti() assert cfg.option('c').defaultmulti()
## assert not list_sessions() ## assert not list_sessions()
@ -135,7 +141,7 @@ def test_symlink_getpermissive():
od1 = OptionDescription('opt', '', [boolopt, linkopt]) od1 = OptionDescription('opt', '', [boolopt, linkopt])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
cfg.option('b').permissive.add('perm') cfg.option('b').permissive.set(frozenset(['perm']))
cfg.option('c').permissive.get() == frozenset(['perm']) cfg.option('c').permissive.get() == frozenset(['perm'])
# assert not list_sessions() # assert not list_sessions()
@ -159,7 +165,7 @@ def test_symlink_getproperties():
od1 = OptionDescription('opt', '', [boolopt, linkopt]) od1 = OptionDescription('opt', '', [boolopt, linkopt])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() cfg.property.read_write()
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test', "validator"} assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test'}
# assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False # assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False
# assert not list_sessions() # assert not list_sessions()
@ -260,26 +266,14 @@ def test_symlink_owner(config_type):
def test_symlink_get_information(): def test_symlink_get_information():
boolopt = BoolOption("b", "", default=False, informations={'test': 'test'})
linkopt = SymLinkOption("c", boolopt)
od1 = OptionDescription('opt', '', [linkopt, boolopt])
cfg = Config(od1)
assert cfg.option('b').information.get('test') == 'test'
assert cfg.option('c').information.get('test') == 'test'
cfg.option('b').information.set('test', 'test2')
assert cfg.option('b').information.get('test') == 'test2'
assert cfg.option('c').information.get('test') == 'test2'
def test_symlink_informations():
boolopt = BoolOption("b", "", default=False) boolopt = BoolOption("b", "", default=False)
with pytest.raises(TypeError):
linkopt = SymLinkOption("c", boolopt, informations={'test': 'test'})
linkopt = SymLinkOption("c", boolopt) linkopt = SymLinkOption("c", boolopt)
od1 = OptionDescription('opt', '', [linkopt, boolopt]) boolopt.impl_set_information('test', 'test')
cfg = Config(od1) assert boolopt.impl_get_information('test') == 'test'
with pytest.raises(ConfigError): assert linkopt.impl_get_information('test') == 'test'
cfg.option('c').information.set('test', 'test2') boolopt.impl_set_information('test', 'test2')
assert boolopt.impl_get_information('test') == 'test2'
assert linkopt.impl_get_information('test') == 'test2'
def test_symlink_leader(): def test_symlink_leader():
@ -291,59 +285,11 @@ def test_symlink_leader():
def test_symlink_followers(): def test_symlink_followers():
a = StrOption('a', "", multi=True)
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 = SymLinkOption('netmask_admin_eth0', ip_admin_eth0) netmask_admin_eth0 = SymLinkOption('netmask_admin_eth0', a)
leader = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) with pytest.raises(ValueError):
od1 = OptionDescription('opt', '', [leader]) Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
cfg = Config(od1)
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': []}
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2'])
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val2'
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'user'
assert parse_od_get(cfg.value.get()) == {
'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1',
'ip_admin_eth0.netmask_admin_eth0': 'val1'},
{'ip_admin_eth0.ip_admin_eth0': 'val2',
'ip_admin_eth0.netmask_admin_eth0': 'val2'}],
}
def test_symlink_leader_default():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", ['val1', 'val2'], multi=True)
netmask_admin_eth0 = SymLinkOption('netmask_admin_eth0', ip_admin_eth0)
leader = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('opt', '', [leader])
cfg = Config(od1)
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val2'
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.isdefault()
assert parse_od_get(cfg.value.get()) == {
'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1',
'ip_admin_eth0.netmask_admin_eth0': 'val1'},
{'ip_admin_eth0.ip_admin_eth0': 'val2',
'ip_admin_eth0.netmask_admin_eth0': 'val2'}],
}
def test_symlink_followers_2():
variable1 = StrOption('variable1', "", multi=True)
variable2 = StrOption('variable2', "", multi=True)
variable3 = SymLinkOption('variable3', variable2)
leader = Leadership('variable1', '', [variable1, variable2, variable3])
od1 = OptionDescription('opt', '', [leader])
cfg = Config(od1)
assert parse_od_get(cfg.value.get()) == {'variable1.variable1': []}
cfg.option('variable1.variable1').value.set(['val1', 'val2'])
cfg.option('variable1.variable2', 0).value.set('ival1')
cfg.option('variable1.variable2', 1).value.set('ival2')
assert cfg.option('variable1.variable3', 1).owner.get() == 'user'
assert cfg.option('variable1.variable3', 1).value.get() == 'ival2'
assert parse_od_get(cfg.value.get()) == {'variable1.variable1': [{'variable1.variable1': 'val1',
'variable1.variable2': 'ival1',
'variable1.variable3': 'ival1'},
{'variable1.variable1': 'val2',
'variable1.variable2': 'ival2',
'variable1.variable3': 'ival2'}],
}
def test_symlink_with_leader(config_type): def test_symlink_with_leader(config_type):
@ -354,9 +300,9 @@ def test_symlink_with_leader(config_type):
od1 = OptionDescription('root', '', [interface1, leader]) od1 = OptionDescription('root', '', [interface1, leader])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [], 'leader': []} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [], 'leader': []}
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2'])
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': None}], 'leader': ['val1', 'val2']} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': None}], 'leader': ['val1', 'val2']}
cfg.option('ip_admin_eth0.ip_admin_eth0').value.pop(0) cfg.option('ip_admin_eth0.ip_admin_eth0').value.pop(0)
with pytest.raises(ConfigError): with pytest.raises(ConfigError):
cfg.option('leader').value.pop(0) cfg.option('leader').value.pop(0)
@ -372,9 +318,9 @@ def test_symlink_with_follower(config_type):
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert not cfg.option('follower').isoptiondescription() assert not cfg.option('follower').isoptiondescription()
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [], 'follower': []} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [], 'follower': []}
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2']) cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2'])
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': None}], 'follower': [None, None]} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': None}], 'follower': [None, None]}
# #
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).owner.get() == 'default' assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).owner.get() == 'default'
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'default' assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'default'
@ -389,7 +335,7 @@ def test_symlink_with_follower(config_type):
assert cfg.option('follower').value.get() == [None, None] assert cfg.option('follower').value.get() == [None, None]
# #
cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val3') cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val3')
assert parse_od_get(cfg.value.get()) == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': 'val3'}], 'follower': [None, 'val3']} assert cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': [{'ip_admin_eth0.ip_admin_eth0': 'val1', 'ip_admin_eth0.netmask_admin_eth0': None}, {'ip_admin_eth0.ip_admin_eth0': 'val2', 'ip_admin_eth0.netmask_admin_eth0': 'val3'}], 'follower': [None, 'val3']}
# #
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == None assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == None
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val3' assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val3'
@ -413,9 +359,9 @@ def test_symlink_dependency():
[linkopt, OptionDescription("s1", "", [boolopt])]) [linkopt, OptionDescription("s1", "", [boolopt])])
cfg = Config(od1) cfg = Config(od1)
assert cfg.option('s1.b').has_dependency() is False assert cfg.option('s1.b').has_dependency() is False
assert cfg.option('c').has_dependency() is False assert cfg.option('c').has_dependency() is True
assert cfg.option('s1.b').has_dependency(False) is True assert cfg.option('s1.b').has_dependency(False) is True
assert cfg.option('c').has_dependency(False) is True assert cfg.option('c').has_dependency(False) is False
# assert not list_sessions() # assert not list_sessions()
@ -426,9 +372,9 @@ def test_symlink_makedict(config_type):
[linkopt, OptionDescription("s1", "", [boolopt])]) [linkopt, OptionDescription("s1", "", [boolopt])])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert parse_od_get(cfg.value.get()) == {'c': False, 's1.b': False} assert cfg.value.dict() == {'c': False, 's1.b': False}
cfg.option('s1.b').value.set(True) cfg.option('s1.b').value.set(True)
assert parse_od_get(cfg.value.get()) == {'c': True, 's1.b': True} assert cfg.value.dict() == {'c': True, 's1.b': True}
# assert not list_sessions() # assert not list_sessions()
@ -436,16 +382,22 @@ def test_symlink_list(config_type):
boolopt = BoolOption("b", "", default=False) boolopt = BoolOption("b", "", default=False)
linkopt = SymLinkOption("c", boolopt) linkopt = SymLinkOption("c", boolopt)
od1 = OptionDescription("opt", "", od1 = OptionDescription("opt", "",
[linkopt, OptionDescription("s1", "", [boolopt])]) [linkopt, OptionDescription("s1", "", [boolopt])])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
assert [opt.path() for opt in cfg.list()] == ['c', 's1'] list_opt = []
for opt in cfg.option.list():
list_opt.append(opt.path())
assert list_opt == ['c']
# #
assert [opt.path() for opt in cfg.option('s1').list()] == ['s1.b'] list_opt = []
for opt in cfg.option.list(recursive=True):
list_opt.append(opt.path())
assert list_opt == ['c', 's1.b']
# assert not list_sessions() # assert not list_sessions()
def test_symlink_submulti(): def test_submulti():
multi = StrOption('multi', '', multi=submulti) multi = StrOption('multi', '', multi=submulti)
multi2 = SymLinkOption('multi2', multi) multi2 = SymLinkOption('multi2', multi)
od1 = OptionDescription('od', '', [multi, multi2]) od1 = OptionDescription('od', '', [multi, multi2])
@ -454,13 +406,3 @@ def test_symlink_submulti():
assert cfg.option('multi').issubmulti() assert cfg.option('multi').issubmulti()
assert cfg.option('multi2').ismulti() assert cfg.option('multi2').ismulti()
assert cfg.option('multi2').issubmulti() assert cfg.option('multi2').issubmulti()
def test_symlink_get_option():
multi = StrOption('multi', '', multi=submulti)
multi2 = SymLinkOption('multi2', multi)
od1 = OptionDescription('od', '', [multi, multi2])
cfg = Config(od1)
option = cfg.option('multi2').option()
assert option.name() == 'multi'
assert option.path() == 'multi'

View file

@ -1,4 +1,4 @@
# Copyright (C) 2012-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -12,67 +12,48 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Configuration management library written in python""" """Configuration management library written in python
from .function import ( """
calc_value, from .function import calc_value, calc_value_property_help, valid_ip_netmask, \
calc_value_property_help, valid_network_netmask, valid_in_network, valid_broadcast, \
valid_ip_netmask, valid_not_equal, function_waiting_for_dict
valid_network_netmask, from .autolib import Calculation, Params, ParamOption, ParamDynOption, ParamSelfOption, \
valid_in_network, ParamValue, ParamIndex, ParamSuffix, ParamInformation, ParamSelfInformation
valid_broadcast,
valid_not_equal,
function_waiting_for_dict,
function_waiting_for_error,
)
from .autolib import (
Calculation,
Params,
ParamOption,
ParamDynOption,
ParamSelfOption,
ParamValue,
ParamIndex,
ParamIdentifier,
ParamInformation,
ParamSelfInformation,
)
from .option import * from .option import *
from .error import ConfigError from .error import ConfigError
from .api import Config, MetaConfig, GroupConfig, MixConfig from .api import Config, MetaConfig, GroupConfig, MixConfig
from .option import __all__ as all_options from .option import __all__ as all_options
from .setting import owners, groups, undefined from .setting import owners, groups, undefined
from .__version__ import __version__
allfuncs = [ allfuncs = ['Calculation',
"Calculation", 'Params',
"Params", 'ParamOption',
"ParamOption", 'ParamDynOption',
"ParamDynOption", 'ParamSelfOption',
"ParamSelfOption", 'ParamValue',
"ParamValue", 'ParamIndex',
"ParamIndex", 'ParamSuffix',
"ParamIdentifier", 'ParamInformation',
"ParamInformation", 'ParamSelfInformation',
"ParamSelfInformation", 'MetaConfig',
"MetaConfig", 'MixConfig',
"MixConfig", 'GroupConfig',
"GroupConfig", 'Config',
"Config", 'ConfigError',
"ConfigError", 'undefined',
"undefined", 'owners',
"owners", 'groups',
"groups", 'calc_value',
"calc_value", 'calc_value_property_help',
"calc_value_property_help", 'valid_ip_netmask',
"valid_ip_netmask", 'valid_network_netmask',
"valid_network_netmask", 'valid_in_network',
"valid_in_network", 'valid_broadcast',
"valid_broadcast", 'function_waiting_for_dict',
"function_waiting_for_dict", ]
"function_waiting_for_error",
]
allfuncs.extend(all_options) allfuncs.extend(all_options)
del all_options del(all_options)
__all__ = tuple(allfuncs) __all__ = tuple(allfuncs)
del allfuncs del(allfuncs)
__version__ = "4.0.1"

View file

@ -1 +0,0 @@
__version__ = "5.2.0"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"cache used by storage" "cache used by storage"
# Copyright (C) 2013-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -19,85 +19,88 @@ from time import time
class Cache: class Cache:
"""cache object""" """cache object
"""
__slots__ = ("_cache",) __slots__ = ('_cache',)
def __init__(self): def __init__(self):
self._cache = {} self._cache = {}
def _get_path_index(self, subconfig): def _get_path_index(self, option_bag):
if subconfig is None: if option_bag is None:
path = None path = None
index = None index = None
else: else:
path = subconfig.path path = option_bag.path
index = subconfig.index index = option_bag.index
return path, index return path, index
def getcache( def getcache(self,
self, option_bag,
subconfig, type_,
type_, expiration=True,
expiration=True, ):
): """get the cache value fot a specified path
"""get the cache value fot a specified path""" """
no_cache = False, None, False no_cache = False, None, False
path, index = self._get_path_index(subconfig) path, index = self._get_path_index(option_bag)
if path not in self._cache or index not in self._cache[path]: if path not in self._cache or index not in self._cache[path]:
return no_cache return no_cache
value, timestamp, validated = self._cache[path][index] value, timestamp, validated = self._cache[path][index]
props = subconfig.config_bag.properties if type_ == 'context_props':
if type_ == "self_props": # cached value is settings properties so value is props
# cached value is self_props props = value
self_props = value self_props = {}
else: else:
self_props = subconfig.properties props = option_bag.config_bag.properties
if "cache" in props or "cache" in self_props: if type_ == 'self_props':
if ( # cached value is self_props
expiration self_props = value
and timestamp else:
and ("expire" in props or "expire" in self_props) self_props = option_bag.properties
): if 'cache' in props or \
'cache' in self_props:
if expiration and timestamp and \
('expire' in props or \
'expire' in self_props):
ntime = int(time()) ntime = int(time())
if timestamp + subconfig.config_bag.expiration_time >= ntime: if timestamp + option_bag.config_bag.expiration_time >= ntime:
return True, value, validated return True, value, validated
else: else:
return True, value, validated return True, value, validated
return no_cache return no_cache
def setcache( def setcache(self,
self, option_bag,
subconfig, val,
val, type_='values',
type_="values", validated=True,
validated=True, ):
):
"""add val in cache for a specified path """add val in cache for a specified path
if follower, add index if follower, add index
""" """
if type_ == "values": if type_ == 'values':
if ( if 'cache' not in option_bag.config_bag.properties and \
"cache" not in subconfig.config_bag.properties 'cache' not in option_bag.properties:
and "cache" not in subconfig.properties
):
return return
elif ( elif (option_bag is None or 'cache' not in option_bag.config_bag.properties) and \
subconfig is None or "cache" not in subconfig.config_bag.properties 'cache' not in val:
) and "cache" not in val:
return return
path, index = self._get_path_index(subconfig) path, index = self._get_path_index(option_bag)
self._cache.setdefault(path, {})[index] = (val, int(time()), validated) self._cache.setdefault(path, {})[index] = (val, int(time()), validated)
def delcache(self, path): def delcache(self, path):
"""reset cache a a specified path""" """reset cache a a specified path
if path in self._cache: """
if path in self._cache:
del self._cache[path] del self._cache[path]
def get_cached(self): def get_cached(self):
"""get cache values""" """get cache values
"""
return self._cache return self._cache
def reset_all_cache(self): def reset_all_cache(self):
"""reset all cache values""" """reset all cache values
"""
self._cache.clear() self._cache.clear()

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2012-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,37 +18,14 @@
import weakref import weakref
from .i18n import _ from .i18n import _
from typing import Literal, Union, Optional
def display_list(lst, separator='and', add_quote=False):
TiramisuErrorCode = Literal[
"option-dynamic",
"option-not-found",
"property-frozen",
"property-error",
"property-mandatory",
"leadership-group_type",
"leadership-wrong_property",
"leadership-force_default_on_freeze",
"leadership-greater",
"leadership-follower-greater",
"leadership-follower-callback-list",
]
def display_list(
lst,
*,
separator: str = "and",
add_quote: bool = False,
sort: bool = True,
) -> str():
if not lst: if not lst:
return '""' return '""'
if separator == "and": if separator == 'and':
separator = _("and") separator = _('and')
elif separator == "or": elif separator == 'or':
separator = _("or") separator = _('or')
if isinstance(lst, tuple) or isinstance(lst, frozenset): if isinstance(lst, tuple) or isinstance(lst, frozenset):
lst = list(lst) lst = list(lst)
if len(lst) == 1: if len(lst) == 1:
@ -62,265 +39,133 @@ def display_list(
for l in lst: for l in lst:
if not isinstance(l, str): if not isinstance(l, str):
l = str(l) l = str(l)
lst_.append(_(l))
lst__ = []
for l in lst_:
if add_quote and not l.startswith('"'): if add_quote and not l.startswith('"'):
l = '"{}"'.format(l) l = '"{}"'.format(l)
lst_.append(_(l)) lst__.append(l)
if sort: lst__.sort()
lst_.sort() last = lst__[-1]
last = lst_[-1] return ', '.join(lst__[:-1]) + _(' {} ').format(separator) + '{}'.format(last)
return ", ".join(lst_[:-1]) + _(" {} ").format(separator) + "{}".format(last)
# Exceptions for an Option # Exceptions for an Option
class PropertiesOptionError(AttributeError): class PropertiesOptionError(AttributeError):
"attempt to access to an option with a property that is not allowed" "attempt to access to an option with a property that is not allowed"
def __init__(self,
def __init__( option_bag,
self, proptype,
subconfig, settings,
proptype, opt_type=None,
settings, name=None,
opt_type=None, orig_opt=None,
name=None, help_properties=None):
help_properties=None,
):
if opt_type: if opt_type:
self._opt_type = opt_type self._opt_type = opt_type
self._name = name self._name = name
self._orig_opt = orig_opt
else: else:
if subconfig.option.impl_is_optiondescription(): if option_bag.option.impl_is_optiondescription():
self._opt_type = "optiondescription" self._opt_type = 'optiondescription'
else: else:
self._opt_type = "option" self._opt_type = 'option'
self._name = subconfig.option.impl_get_display_name( self._name = option_bag.option.impl_get_display_name()
subconfig, with_quote=True
)
self._orig_opt = None self._orig_opt = None
self.subconfig = subconfig self._option_bag = option_bag
self.proptype = proptype self.proptype = proptype
self.help_properties = help_properties self.help_properties = help_properties
self._settings = settings self._settings = settings
self.msg = None self.msg = None
if not self.help_properties:
self.help_properties = self.proptype
properties = list(self.help_properties)
if properties == ["frozen"]:
self.code = "property-frozen"
elif properties == ["mandatory"]:
self.code = "property-mandatory"
else:
self.code = "property-error"
super().__init__(None) super().__init__(None)
def display_properties(self, force_property=False, add_quote=True): def set_orig_opt(self, opt):
if force_property: self._orig_opt = opt
properties = self.proptype
else:
properties = self.help_properties
return display_list(list(properties), add_quote=add_quote)
def __str__(self): def __str__(self):
# this part is a bit slow, so only execute when display # this part is a bit slow, so only execute when display
if self.msg is not None: if self.msg is not None:
return self.msg return self.msg
if self._settings is None: if self._settings is None:
return "error" return 'error'
arguments = [self._opt_type] if self.help_properties:
if self._orig_opt: properties = list(self.help_properties)
arguments.append(
self._orig_opt.impl_get_display_name(subconfig, with_quote=True)
)
arguments.append(self._name)
index = self.subconfig.index
if index is not None:
arguments.append(index)
if self.code == "property-frozen":
if index is not None:
if self._orig_opt:
msg = _(
'cannot modify the {0} {1} at index "{2}" because {3} is frozen'
)
else:
msg = _(
'cannot modify the {0} {1} at index "{2}" because is frozen'
)
else:
if self._orig_opt:
msg = _("cannot modify the {0} {1} because {2} is frozen")
else:
msg = _("cannot modify the {0} {1} because is frozen")
elif self.code == "property-mandatory":
if index is not None:
if self._orig_opt:
msg = _(
'cannot access to {0} {1} at index "{2}" because {3} hasn\'t value'
)
else:
msg = _('{0} {1} at index "{2}" is mandatory but hasn\'t value')
else:
if self._orig_opt:
msg = _("cannot access to {0} {1} because {2} hasn't value")
else:
msg = _("{0} {1} is mandatory but hasn't value")
else: else:
if index is not None: properties = list(self.proptype)
if self._orig_opt: only_one = len(properties) == 1
msg = _( properties_msg = display_list(properties, add_quote=True)
'cannot access to {0} {1} at index "{2}" because {3} has {4} {5}' if only_one:
) prop_msg = _('property')
else: else:
msg = _( prop_msg = _('properties')
'cannot access to {0} {1} at index "{2}" because has {3} {4}' if properties == ['frozen']:
) if self._orig_opt:
msg = 'cannot modify the {0} "{1}" because "{2}" has {3} {4}'
else: else:
if self._orig_opt: msg = 'cannot modify the {0} "{1}" because has {2} {3}'
msg = _("cannot access to {0} {1} because {2} has {3} {4}") else:
else: if self._orig_opt:
msg = _("cannot access to {0} {1} because has {2} {3}") msg = 'cannot access to {0} "{1}" because "{2}" has {3} {4}'
only_one = len(self.help_properties) == 1
if only_one:
arguments.append(_("property"))
else: else:
arguments.append(_("properties")) msg = 'cannot access to {0} "{1}" because has {2} {3}'
arguments.append(self.display_properties()) if self._orig_opt:
self.msg = msg.format(*arguments) self.msg = _(msg).format(self._opt_type,
self._orig_opt.impl_get_display_name(),
self._name,
prop_msg,
properties_msg)
else:
self.msg = _(msg).format(self._opt_type,
self._name,
prop_msg,
properties_msg)
del self._opt_type, self._name del self._opt_type, self._name
del self._settings, self._orig_opt del self._settings, self._orig_opt
return self.msg return self.msg
class AttributeOptionError(AttributeError): #____________________________________________________________
def __init__(self, path: str, code: TiramisuErrorCode) -> None:
self.path = path
self.code = code
def __str__(self) -> str:
if self.code == "option-dynamic":
return _('cannot access to "{0}" it\'s a dynamic option').format(self.path)
return _('"{0}" is not an option').format(self.path)
# ____________________________________________________________
# Exceptions for a Config # Exceptions for a Config
class ConfigError(Exception): class ConfigError(Exception):
"""attempt to change an option's owner without a value """attempt to change an option's owner without a value
or in case of `_descr` is None or in case of `_descr` is None
or if a calculation cannot be carried out""" or if a calculation cannot be carried out"""
def __init__(self,
def __init__( exp,
self, ori_err=None,
exp, ):
*,
prefix: Optional[str] = None,
subconfig: Optional["Subconfig"] = None,
):
super().__init__(exp) super().__init__(exp)
self.err_msg = exp self.ori_err = ori_err
self.subconfig = subconfig
self.prefix = prefix
def __str__(self):
msg = self.prefix
if msg:
msg += ", {}".format(self.err_msg)
else:
msg = self.err_msg
return msg
class ConflictError(Exception): class ConflictError(Exception):
"duplicate options are present in a single config" "duplicate options are present in a single config"
pass pass
# ____________________________________________________________ #____________________________________________________________
# miscellaneous exceptions # miscellaneous exceptions
class LeadershipError(Exception): class LeadershipError(Exception):
def __init__( "problem with a leadership's value length"
self, pass
subconfig: Union[str, "SubConfig"],
code,
*,
prop=None,
index=None,
length=None,
callback=None,
args=None,
kwargs=None,
ret=None,
):
if isinstance(subconfig, str):
self.path = self.display_name = subconfig
else:
self.path = subconfig.path
option = subconfig.option
self.display_name = option.impl_get_display_name(subconfig, with_quote=True)
self.code = code
if prop is not None:
self.prop = prop
if index is not None:
self.index = index
if length is not None:
self.length = length
if callback is not None:
self.callback = callback
if args is not None:
self.args = args
if kwargs is not None:
self.kwargs = kwargs
if ret is not None:
self.ret = ret
def __str__(self):
if self.code == "leadership-group_type":
return _('cannot set "group_type" attribute for the Leadership {0}').format(
self.display_name
)
if self.code == "leadership-wrong_property":
return _('the leader {0} cannot have "{1}" property').format(
self.display_name, self.prop
)
if self.code == "leadership-force_default_on_freeze":
return _(
'the leader {0} cannot have "force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
).format(self.display_name)
if self.code == "leadership-reduce":
return _("cannot reduce length of the leader {0}").format(self.display_name)
if self.code == "leadership-greater":
return _(
'index "{0}" is greater than the leadership length "{1}" for option {2}'
).format(self.index, self.length, self.display_name)
if self.code == "leadership-follower-greater":
return _(
"the follower option {0} has greater length ({1}) than the leader length ({2})"
).format(self.display_name, self.index, self.length)
if self.code == "leadership-follower-callback-list":
if self.args or self.kwargs:
return _(
'the "{0}" function with positional arguments "{1}" and keyword arguments "{2}" must not return a list ("{3}") for the follower option {4}'
).format(
self.callback, self.args, self.kwargs, self.ret, self.display_name
)
return _(
'the "{0}" function must not return a list ("{1}") for the follower option {2}'
).format(self.callback, self.ret, self.display_name)
class ConstError(TypeError): class ConstError(TypeError):
"no uniq value in _NameSpace" "no uniq value in _NameSpace"
pass pass
class _CommonError: class _CommonError:
def __init__(self, subconfig, val, display_type, opt, err_msg, index): def __init__(self,
val,
display_type,
opt,
err_msg,
index):
self.val = val self.val = val
self.display_type = display_type self.display_type = display_type
self.opt = weakref.ref(opt) self.opt = weakref.ref(opt)
self.name = opt.impl_get_display_name(subconfig, with_quote=True) self.name = opt.impl_get_display_name()
if subconfig:
self.path = subconfig.path
self.err_msg = err_msg self.err_msg = err_msg
self.index = index self.index = index
super().__init__(self.err_msg) super().__init__(self.err_msg)
@ -329,37 +174,28 @@ class _CommonError:
try: try:
msg = self.prefix msg = self.prefix
except AttributeError: except AttributeError:
self.prefix = self.tmpl.format( self.prefix = self.tmpl.format(self.val,
self.val, _(self.display_type), self.name, self.index self.display_type,
) self.name)
msg = self.prefix msg = self.prefix
if self.err_msg: if self.err_msg:
if msg: if msg:
msg += ", {}".format(self.err_msg) msg += ', {}'.format(self.err_msg)
else: else:
msg = self.err_msg msg = self.err_msg
if not msg: if not msg:
msg = _("invalid value") msg = _('invalid value')
return msg return msg
class ValueWarning(_CommonError, UserWarning): class ValueWarning(_CommonError, UserWarning):
tmpl = None tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
def __init__(self, **kwargs): def __init__(self, *args, **kwargs):
if ValueWarning.tmpl is None: if len(args) == 1 and not kwargs:
if kwargs.get("index") is None: self.msg = args[0]
ValueWarning.tmpl = _(
'attention, "{0}" could be an invalid {1} for {2}'
)
else:
ValueWarning.tmpl = _(
'attention, "{0}" could be an invalid {1} for {2} at index "{3}"'
)
if list(kwargs) == ["msg"]:
self.msg = kwargs["msg"]
else: else:
super().__init__(**kwargs) super().__init__(*args, **kwargs)
self.msg = None self.msg = None
def __str__(self): def __str__(self):
@ -369,69 +205,8 @@ class ValueWarning(_CommonError, UserWarning):
class ValueOptionError(_CommonError, ValueError): class ValueOptionError(_CommonError, ValueError):
tmpl = None tmpl = _('"{0}" is an invalid {1} for "{2}"')
def __init__(self, **kwargs):
if ValueOptionError.tmpl is None:
opt = kwargs.get("opt")
if opt and opt._do_not_display_value_in_error:
if kwargs.get("index") is None:
self.tmpl = _("{2} has an invalid {1}")
else:
self.tmpl = _('{2} at index "{3}" has an invalid {1}')
else:
if kwargs.get("index") is None:
self.tmpl = _('"{0}" is an invalid {1} for {2}')
else:
self.tmpl = _('"{0}" is an invalid {1} for {2} at index "{3}"')
super().__init__(**kwargs)
class ValueErrorWarning(ValueWarning): class ValueErrorWarning(ValueWarning):
tmpl = None tmpl = _('"{0}" is an invalid {1} for "{2}"')
def __init__(self, *args, **kwargs):
if ValueErrorWarning.tmpl is None:
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for {2}')
super().__init__(*args, **kwargs)
class CancelParam(Exception):
def __init__(self, origin_path, current_path):
super().__init__()
self.origin_path = origin_path
self.current_path = current_path
def __ne__(self, value):
return value is None or value == ""
def __eq__(self, value):
return value is None or value == ""
def __bool__(self):
return False
class ValueErrorIndexes(ValueError):
def __init__(self, msg, indexes):
super().__init__(msg)
self.indexes = indexes
class Errors:
@staticmethod
def raise_carry_out_calculation_error(
subconfig, message, original_error, option=None, extra_keys=[]
):
if option is None:
option = subconfig.option
display_name = option.impl_get_display_name(subconfig, with_quote=True)
if original_error:
raise ConfigError(
message.format(display_name, original_error, *extra_keys),
subconfig=subconfig,
) from original_error
raise ConfigError(message.format(display_name, extra_keys), subconfig=subconfig)
errors = Errors()

View file

@ -1,4 +1,4 @@
# Copyright (C) 2018-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2018-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -12,7 +12,8 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""some functions to validates or calculates value""" """some functions to validates or calculates value
"""
from typing import Any, List, Optional from typing import Any, List, Optional
from operator import add, mul, sub, truediv from operator import add, mul, sub, truediv
from ipaddress import ip_address, ip_interface, ip_network from ipaddress import ip_address, ip_interface, ip_network
@ -22,12 +23,11 @@ from .error import display_list
FUNCTION_WAITING_FOR_DICT = [] FUNCTION_WAITING_FOR_DICT = []
FUNCTION_WAITING_FOR_ERROR = []
def function_waiting_for_dict(function): def function_waiting_for_dict(function):
"""functions (calculation or validation) receive by default only the value of other options """functions (calculation or validation) receive by default only the value of other options
if you use this decoractor, it will recieve a dict with option informations all functions declared with this function recieve a dict with option informations
(value, name, ...) (value, name, ...)
""" """
name = function.__name__ name = function.__name__
@ -36,94 +36,74 @@ def function_waiting_for_dict(function):
return function return function
def function_waiting_for_error(function):
"""functions (calculation or validation) receiven by default, only the value of other options
if you use this decoractor, it will pass PropertyError too
"""
name = function.__name__
if name not in FUNCTION_WAITING_FOR_ERROR:
FUNCTION_WAITING_FOR_ERROR.append(name)
return function
@function_waiting_for_dict @function_waiting_for_dict
def valid_network_netmask( def valid_network_netmask(network: dict,
network: dict, netmask: dict,
netmask: dict, ):
):
""" """
validates if network and netmask are coherent validates if network and netmask are coherent
this validator must be set to netmask option this validator must be set to netmask option
""" """
if None in [network["value"], netmask["value"]]: if None in [network['value'], netmask['value']]:
return return
try: try:
ip_network(f'{network["value"]}/{netmask["value"]}') ip_network(f'{network["value"]}/{netmask["value"]}')
except ValueError as err: except ValueError as err:
raise ValueError( raise ValueError(_(f'network "{network["value"]}" ({network["name"]}) does not match '
_('network "{0}" ({1}) does not match with this netmask').format( 'with this netmask')) from err
network["value"], network["name"]
)
) from err
@function_waiting_for_dict @function_waiting_for_dict
def valid_ip_netmask( def valid_ip_netmask(ip: dict, # pylint: disable=invalid-name
ip: dict, # pylint: disable=invalid-name netmask: dict,
netmask: dict, ):
):
"""validates if ip and netmask are coherent """validates if ip and netmask are coherent
this validator must be set to netmask option this validator must be set to netmask option
""" """
if None in [ip["value"], netmask["value"]]: if None in [ip['value'], netmask['value']]:
return return
ip_netmask = ip_interface(f'{ip["value"]}/{netmask["value"]}') ip_netmask = ip_interface(f'{ip["value"]}/{netmask["value"]}')
if ip_netmask.ip == ip_netmask.network.network_address: if ip_netmask.ip == ip_netmask.network.network_address:
msg = _('IP "{0}" ({1}) with this netmask is in fact a network address').format( msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
ip["value"], ip["name"] 'in fact a network address')
)
raise ValueError(msg) raise ValueError(msg)
if ip_netmask.ip == ip_netmask.network.broadcast_address: if ip_netmask.ip == ip_netmask.network.broadcast_address:
msg = _( msg = _(f'IP "{ip["value"]}" ({ip["name"]}) with this netmask is '
'IP "{0}" ({1}) with this netmask is in fact a broadcast address' 'in fact a broacast address')
).format(ip["value"], ip["name"])
raise ValueError(msg) raise ValueError(msg)
@function_waiting_for_dict @function_waiting_for_dict
def valid_broadcast( def valid_broadcast(network: dict,
network: dict, netmask: dict,
netmask: dict, broadcast: dict,
broadcast: dict, ):
): """validates if the broadcast is coherent with network and netmask
"""validates if the broadcast is coherent with network and netmask""" """
if None in [network["value"], netmask["value"], broadcast["value"]]: if None in [network['value'], netmask['value'], broadcast['value']]:
return return
if ip_network( if ip_network(f'{network["value"]}/{netmask["value"]}').broadcast_address != \
f'{network["value"]}/{netmask["value"]}' ip_address(broadcast['value']):
).broadcast_address != ip_address(broadcast["value"]): msg = _(f'broadcast invalid with network {network["value"]} ({network["name"]}) '
msg = _( f'and netmask {netmask["value"]} ({netmask["name"]})')
"broadcast invalid with network {0} ({1}) and netmask {2} ({3})"
).format(network["value"], network["name"], netmask["value"], netmask["name"])
raise ValueError(msg) raise ValueError(msg)
@function_waiting_for_dict @function_waiting_for_dict
def valid_in_network( def valid_in_network(ip: dict, # pylint: disable=invalid-name
ip: dict, # pylint: disable=invalid-name network: dict,
network: dict, netmask=Optional[dict],
netmask=Optional[dict], ):
):
"""validates if an IP is in a network """validates if an IP is in a network
this validator must be set to ip option this validator must be set to ip option
""" """
if None in [ip["value"], network["value"]]: if None in [ip['value'], network['value']]:
return return
if "/" in network["value"]: if '/' in network['value']:
# it's a CIDR network # it's a CIDR network
network_value = network["value"] network_value = network['value']
else: else:
if netmask is None or netmask["value"] is None: if netmask is None or netmask['value'] is None:
return return
network_value = f'{network["value"]}/{netmask["value"]}' network_value = f'{network["value"]}/{netmask["value"]}'
network_obj = ip_network(network_value) network_obj = ip_network(network_value)
@ -132,63 +112,56 @@ def valid_in_network(
if netmask is None: if netmask is None:
msg = _('this IP is not in network {network["value"]} ({network["name"]})') msg = _('this IP is not in network {network["value"]} ({network["name"]})')
else: else:
msg = _( msg = _('this IP is not in network {network["value"]} ({network["name"]}) '
'this IP is not in network {network["value"]} ({network["name"]}) ' 'with netmask {netmask["value"]} ({netmask["name"]})')
'with netmask {netmask["value"]} ({netmask["name"]})'
)
raise ValueError(msg) raise ValueError(msg)
# test if ip is not network/broadcast IP # test if ip is not network/broadcast IP
if ip_netmask.ip == ip_netmask.network.network_address: if ip_netmask.ip == ip_netmask.network.network_address:
msg = _( msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
"this IP with the network {0} ({1}) is in fact a network address" 'is in fact a network address')
).format(network["value"], network["name"])
raise ValueError(msg) raise ValueError(msg)
if ip_netmask.ip == ip_netmask.network.broadcast_address: if ip_netmask.ip == ip_netmask.network.broadcast_address:
msg = _( msg = _(f'this IP with the network {network["value"]} ({network["value"]} '
"this IP with the network {0} ({1}) is in fact a broadcast address" 'is in fact a broadcast address')
).format(network["value"], network["value"])
raise ValueError(msg) raise ValueError(msg)
@function_waiting_for_dict @function_waiting_for_dict
def valid_not_equal(*values): def valid_not_equal(*values):
"""valid that two options have not same value""" """valid that two options have not same value
"""
equal = set() equal = set()
for val in values[1:]: for val in values[1:]:
if "propertyerror" in val: if 'propertyerror' in val:
continue continue
if values[0]["value"] == val["value"] is not None: if values[0]['value'] == val['value'] is not None:
equal.add(val["name"]) equal.add(val['name'])
if not equal: if not equal:
return return
msg = _("value is identical to {0}").format( msg = _(f'value is identical to {display_list(list(equal), add_quote=True)}')
display_list(list(equal), add_quote=True)
)
raise ValueError(msg) raise ValueError(msg)
class CalcValue: class CalcValue:
"""class to calc_value with different functions""" """class to calc_value with different functions
"""
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
def __call__( def __call__(self,
self, *args: List[Any],
*args: List[Any], multi: bool=False,
multi: bool = False, default: Any=undefined,
default: Any = undefined, condition: Any=undefined,
condition: Any = undefined, no_condition_is_invalid: bool=False,
no_condition_is_invalid: bool = False, expected: Any=undefined,
expected: Any = undefined, condition_operator: str='AND',
condition_operator: str = "AND", reverse_condition: bool=False,
reverse_condition: bool = False, allow_none: bool=False,
allow_none: bool = False, remove_duplicate_value: bool=False,
remove_duplicate_value: bool = False, join: Optional[str]=None,
join: Optional[str] = None, min_args_len: Optional[int]=None,
min_args_len: Optional[int] = None, operator: Optional[str]=None,
operator: Optional[str] = None, index: Optional[int]=None,
index: Optional[int] = None, **kwargs) -> Any:
**kwargs,
) -> Any:
# pylint: disable=too-many-statements,too-many-branches,too-many-nested-blocks,too-many-locals # pylint: disable=too-many-statements,too-many-branches,too-many-nested-blocks,too-many-locals
"""calculate value """calculate value
:param args: list of value :param args: list of value
@ -352,13 +325,10 @@ class CalcValue:
self.condition_operator = condition_operator self.condition_operator = condition_operator
self.reverse_condition = reverse_condition self.reverse_condition = reverse_condition
self.kwargs = kwargs self.kwargs = kwargs
self.no_condition_is_invalid = ( self.no_condition_is_invalid = no_condition_is_invalid # pylint: disable=attribute-defined-outside-init
no_condition_is_invalid # pylint: disable=attribute-defined-outside-init value = self.get_value(default,
) min_args_len,
value = self.get_value( )
default,
min_args_len,
)
if not multi: if not multi:
if join is not None: if join is not None:
if None not in value: if None not in value:
@ -367,12 +337,11 @@ class CalcValue:
value = None value = None
elif value and operator: elif value and operator:
new_value = value[0] new_value = value[0]
oper = { oper = {'mul': mul,
"mul": mul, 'add': add,
"add": add, 'div': truediv,
"div": truediv, 'sub': sub,
"sub": sub, }[operator]
}[operator]
for val in value[1:]: for val in value[1:]:
new_value = oper(new_value, val) new_value = oper(new_value, val)
value = new_value value = new_value
@ -396,9 +365,8 @@ class CalcValue:
break break
lval = len(val) lval = len(val)
if length_val is not None and length_val != lval: if length_val is not None and length_val != lval:
msg = _( msg = _('unexpected value in calc_value with join attribute '
'unexpected value in calc_value with join attribute "{0}" with invalid length "{1}"' f'"{val}" with invalid length "{length_val}"')
).format(val, length_val)
raise ValueError(msg) raise ValueError(msg)
length_val = lval length_val = lval
new_value = [] new_value = []
@ -424,16 +392,19 @@ class CalcValue:
value = new_value value = new_value
return value return value
def value_from_kwargs( def value_from_kwargs(self,
self, value: Any, pattern: str, to_dict: bool = False, empty_test=undefined value: Any,
) -> Any: pattern: str,
"""get value from kwargs""" to_dict: bool=False,
empty_test=undefined) -> Any:
"""get value from kwargs
"""
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
# if value attribute exist return it's value # if value attribute exist return it's value
# otherwise pattern_0, pattern_1, ... # otherwise pattern_0, pattern_1, ...
# otherwise undefined # otherwise undefined
if value is not empty_test: if value is not empty_test:
if to_dict == "all": if to_dict == 'all':
returns = {None: value} returns = {None: value}
else: else:
returns = value returns = value
@ -444,7 +415,7 @@ class CalcValue:
if key.startswith(pattern): if key.startswith(pattern):
index = int(key[len_pattern:]) index = int(key[len_pattern:])
if isinstance(pattern_value, dict): if isinstance(pattern_value, dict):
pattern_value = pattern_value["value"] pattern_value = pattern_value['value']
kwargs_matches[index] = pattern_value kwargs_matches[index] = pattern_value
if not kwargs_matches: if not kwargs_matches:
returns = undefined returns = undefined
@ -461,47 +432,38 @@ class CalcValue:
returns.append(kwargs_matches[key]) returns.append(kwargs_matches[key])
return returns return returns
def is_condition_matches( def is_condition_matches(self,
self, condition_value,
condition_value, ):
): """verify the condition
"""verify the condition""" """
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
calculated_conditions = self.value_from_kwargs( calculated_conditions = self.value_from_kwargs(condition_value,
condition_value, 'condition_',
"condition_", to_dict='all',
to_dict="all", )
)
if calculated_conditions is undefined: if calculated_conditions is undefined:
is_matches = not self.no_condition_is_invalid is_matches = not self.no_condition_is_invalid
else: else:
is_matches = None is_matches = None
calculated_expected = self.value_from_kwargs( calculated_expected = self.value_from_kwargs(self.expected,
self.expected, 'expected_',
"expected_", to_dict=True,
to_dict=True, )
) calculated_reverse = self.value_from_kwargs(self.reverse_condition,
calculated_reverse = self.value_from_kwargs( 'reverse_condition_',
self.reverse_condition, to_dict=True,
"reverse_condition_", empty_test=False,
to_dict=True, )
empty_test=False,
)
for idx, calculated_condition in calculated_conditions.items(): for idx, calculated_condition in calculated_conditions.items():
if isinstance(calculated_expected, dict): if isinstance(calculated_expected, dict):
if idx is not None: if idx is not None:
if isinstance(calculated_expected[idx], list): if isinstance(calculated_expected[idx], list):
current_matches = ( current_matches = calculated_condition in calculated_expected[idx]
calculated_condition in calculated_expected[idx]
)
else: else:
current_matches = ( current_matches = calculated_condition == calculated_expected[idx]
calculated_condition == calculated_expected[idx]
)
else: else:
current_matches = ( current_matches = calculated_condition in calculated_expected.values()
calculated_condition in calculated_expected.values()
)
else: else:
current_matches = calculated_condition == calculated_expected current_matches = calculated_condition == calculated_expected
if isinstance(calculated_reverse, dict) and idx in calculated_reverse: if isinstance(calculated_reverse, dict) and idx in calculated_reverse:
@ -510,41 +472,36 @@ class CalcValue:
reverse_condition = False reverse_condition = False
if is_matches is None: if is_matches is None:
is_matches = current_matches is_matches = current_matches
if self.condition_operator == "AND": if self.condition_operator == 'AND':
is_matches = is_matches and current_matches is_matches = is_matches and current_matches
if reverse_condition: if reverse_condition:
is_matches = not is_matches is_matches = not is_matches
if not is_matches: if not is_matches:
break break
elif self.condition_operator == "OR": elif self.condition_operator == 'OR':
is_matches = is_matches or current_matches is_matches = is_matches or current_matches
if reverse_condition: if reverse_condition:
is_matches = not is_matches is_matches = not is_matches
if is_matches: if is_matches:
break break
else: else:
msg = _( msg = _(f'unexpected {self.condition_operator} condition_operator '
"unexpected {0} condition_operator " "in calc_value" 'in calc_value')
).format(self.condition_operator)
raise ValueError(msg) raise ValueError(msg)
is_matches = ( is_matches = is_matches and not self.reverse_condition \
is_matches or not is_matches and self.reverse_condition
and not self.reverse_condition
or not is_matches
and self.reverse_condition
)
return is_matches return is_matches
def get_value( def get_value(self,
self, default,
default, min_args_len,
min_args_len, ):
): """get the value from arguments
"""get the value from arguments""" """
# retrieve the condition # retrieve the condition
if isinstance(self.condition, dict): if isinstance(self.condition, dict):
if "value" in self.condition: if 'value' in self.condition:
condition_value = self.condition["value"] condition_value = self.condition['value']
else: else:
condition_value = undefined condition_value = undefined
else: else:
@ -559,10 +516,9 @@ class CalcValue:
value = [] value = []
if not value: if not value:
# default value # default value
new_default = self.value_from_kwargs( new_default = self.value_from_kwargs(default,
default, 'default_',
"default_", )
)
if new_default is not undefined: if new_default is not undefined:
if not isinstance(new_default, list): if not isinstance(new_default, list):
value = [new_default] value = [new_default]
@ -571,32 +527,34 @@ class CalcValue:
return value return value
def get_args(self): def get_args(self):
"""get all arguments""" """get all arguments
"""
return list(self.args) return list(self.args)
class CalcValuePropertyHelp(CalcValue): class CalcValuePropertyHelp(CalcValue):
"""special class to display property error""" """special class to display property error
"""
def get_name(self): def get_name(self):
"""get the condition name""" """get the condition name
return self.condition["name"] """
return self.condition['name']
def get_indexed_name(self, index: int) -> str: def get_indexed_name(self, index: int) -> str:
"""get name for a specified index""" """get name for a specified index
condition_index = self.kwargs.get(f"condition_{index}") """
condition_index = self.kwargs.get(f'condition_{index}')
if condition_index is not None and not isinstance(condition_index, dict): if condition_index is not None and not isinstance(condition_index, dict):
raise ValueError( raise ValueError(_(f'unexpected condition_{index} must have "todict" argument'))
_('unexpected condition_{0} must have "todict" argument').format(index) return condition_index['name']
)
return condition_index["name"]
def build_property_message(
self, def build_property_message(self,
name: str, name: str,
value: Any, value: Any,
) -> str: ) -> str:
"""prepare message to display error message if needed""" """prepare message to display error message if needed
"""
if not self.reverse_condition: if not self.reverse_condition:
msg = _('the value of "{0}" is {1}').format(name, value) msg = _('the value of "{0}" is {1}').format(name, value)
else: else:
@ -606,36 +564,34 @@ class CalcValuePropertyHelp(CalcValue):
def get_args(self): def get_args(self):
args = super().get_args() args = super().get_args()
action = args[0] action = args[0]
calculated_expected = self.value_from_kwargs( calculated_expected = self.value_from_kwargs(self.expected,
self.expected, "expected_", to_dict=True 'expected_',
) to_dict=True)
if self.condition is not undefined: if self.condition is not undefined:
if "propertyerror" in self.condition: if 'propertyerror' in self.condition:
msg = self.condition["propertyerror"] msg = self.condition['propertyerror']
else: else:
name = self.get_name() name = self.get_name()
if isinstance(calculated_expected, dict): if isinstance(calculated_expected, dict):
calc_values = calculated_expected.values() calc_values = calculated_expected.values()
else: else:
calc_values = [calculated_expected] calc_values = [calculated_expected]
display_value = display_list( display_value = display_list([str(val) for val in calc_values],
[str(val) for val in calc_values], separator="or", add_quote=True 'or',
) add_quote=True)
msg = self.build_property_message(name, display_value) msg = self.build_property_message(name, display_value)
else: else:
msgs = [] msgs = []
for key, value in calculated_expected.items(): for key, value in calculated_expected.items():
name = self.get_indexed_name(key) name = self.get_indexed_name(key)
msgs.append(self.build_property_message(name, f'"{value}"')) msgs.append(self.build_property_message(name, f'"{value}"'))
msg = display_list(msgs, separator=self.condition_operator.lower()) msg = display_list(msgs, self.condition_operator.lower())
return [(action, f'"{action}" ({msg})')] return [(action, f'"{action}" ({msg})')]
calc_value = CalcValue() calc_value = CalcValue()
calc_value.__name__ = "calc_value" # pylint: disable=attribute-defined-outside-init calc_value.__name__ = 'calc_value' # pylint: disable=attribute-defined-outside-init
# function_waiting_for_dict(calc_value) # function_waiting_for_dict(calc_value)
calc_value_property_help = CalcValuePropertyHelp() calc_value_property_help = CalcValuePropertyHelp()
calc_value_property_help.__name__ = ( calc_value_property_help.__name__ = 'calc_value_property_help' # pylint: disable=attribute-defined-outside-init
"calc_value_property_help" # pylint: disable=attribute-defined-outside-init
)
function_waiting_for_dict(calc_value_property_help) function_waiting_for_dict(calc_value_property_help)

View file

@ -1,5 +1,5 @@
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
# Copyright (C) 2012-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,9 +18,55 @@
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
"internationalisation utilities" "internationalisation utilities"
from gettext import translation from .log import log
from pathlib import Path
t = translation("tiramisu", str(Path(__file__).parent / "locale"), fallback=True) from gettext import translation, NullTranslations
from platform import system
from pkg_resources import resource_filename
from os import environ
_ = t.gettext
DEFAULT = 'en'
def get_translation() -> str:
"""Sets the user locale as langage
The default is set to english
"""
# Application name (without .i18n)
app_name = __name__[:-5]
translations_path = resource_filename(app_name, 'locale')
if 'TIRAMISU_LOCALE' in environ: # pragma: no cover
user_locale = environ['TIRAMISU_LOCALE']
else:
if 'Windows' in system(): # pragma: no cover
import ctypes
from locale import windows_locale
default_locale = windows_locale[ctypes.windll.kernel32.GetUserDefaultUILanguage()]
else:
from locale import getlocale
default_locale = getlocale()
if default_locale and isinstance(default_locale, tuple):
if default_locale[0] is not None:
user_locale = default_locale[0][:2]
else:
user_locale = DEFAULT
elif default_locale: # pragma: no cover
user_locale = default_locale[:2]
else: # pragma: no cover
user_locale = DEFAULT
try:
trans = translation(domain=app_name,
localedir=translations_path,
languages=[user_locale],
)
# codeset='UTF-8')
except FileNotFoundError: # pragma: no cover
log.debug('cannot found translation file for langage {} in localedir {}'.format(user_locale,
translations_path))
trans = NullTranslations()
return trans.gettext
_ = get_translation()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,711 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2023-11-19 21:26+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: tiramisu/api.py:61
msgid "Settings:"
msgstr ""
#: tiramisu/api.py:63
msgid "Access to option without verifying permissive properties"
msgstr ""
#: tiramisu/api.py:67
msgid "Access to option without property restriction"
msgstr ""
#: tiramisu/api.py:70
msgid "Do not warnings during validation"
msgstr ""
#: tiramisu/api.py:75
msgid "Call: {}"
msgstr ""
#: tiramisu/api.py:77
msgid "Commands:"
msgstr ""
#: tiramisu/api.py:185
msgid "unknown list type {}"
msgstr ""
#: tiramisu/api.py:187
msgid "unknown group_type: {0}"
msgstr ""
#: tiramisu/api.py:352
msgid "only multi value has defaultmulti"
msgstr ""
#: tiramisu/api.py:371 tiramisu/option/intoption.py:31
msgid "integer"
msgstr ""
#: tiramisu/api.py:374 tiramisu/option/domainnameoption.py:43
msgid "domain name"
msgstr ""
#: tiramisu/api.py:376
msgid "ip"
msgstr ""
#: tiramisu/api.py:376
msgid "netmask"
msgstr ""
#: tiramisu/api.py:376
msgid "network"
msgstr ""
#: tiramisu/api.py:471
msgid "cannot add this property: \"{0}\""
msgstr ""
#: tiramisu/api.py:621
msgid "cannot reduce length of the leader \"{}\""
msgstr ""
#: tiramisu/api.py:1088
msgid "properties must be a frozenset"
msgstr ""
#: tiramisu/api.py:1094 tiramisu/api.py:1118
msgid "unknown when {} (must be in append or remove)"
msgstr ""
#: tiramisu/api.py:1106 tiramisu/api.py:1127 tiramisu/config.py:1210
msgid "unknown type {}"
msgstr ""
#: tiramisu/api.py:1432
msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr ""
#: tiramisu/autolib.py:44
msgid "args in params must be a tuple"
msgstr ""
#: tiramisu/autolib.py:47 tiramisu/autolib.py:52
msgid "arg in params must be a Param"
msgstr ""
#: tiramisu/autolib.py:49
msgid "kwargs in params must be a dict"
msgstr ""
#: tiramisu/autolib.py:72
msgid "paramoption needs an option not {}"
msgstr ""
#: tiramisu/autolib.py:77
msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:78
msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:133
msgid "option in ParamInformation cannot be a symlinkoption"
msgstr ""
#: tiramisu/autolib.py:135
msgid "option in ParamInformation cannot be a follower"
msgstr ""
#: tiramisu/autolib.py:137
msgid "option in ParamInformation cannot be a dynamic option"
msgstr ""
#: tiramisu/autolib.py:166
msgid "first argument ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:168
msgid "help_function ({0}) must be a function"
msgstr ""
#: tiramisu/autolib.py:316 tiramisu/autolib.py:362
msgid "unable to carry out a calculation for \"{}\", {}"
msgstr ""
#: tiramisu/autolib.py:319 tiramisu/autolib.py:365
msgid "the option \"{0}\" is used in a calculation but is invalid ({1})"
msgstr ""
#: tiramisu/autolib.py:402
msgid "option \"{}\" cannot be calculated: {}"
msgstr ""
#: tiramisu/autolib.py:411
msgid "option \"{}\" is not in a dynoptiondescription"
msgstr ""
#: tiramisu/autolib.py:562
msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option \"{}\""
msgstr ""
#: tiramisu/autolib.py:571
msgid "the \"{}\" function must not return a list (\"{}\") for the follower option \"{}\""
msgstr ""
#: tiramisu/autolib.py:604
msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option \"{2}\""
msgstr ""
#: tiramisu/autolib.py:611
msgid "unexpected error \"{0}\" in function \"{1}\" for option \"{2}\""
msgstr ""
#: tiramisu/config.py:82
msgid "there is no option description for this config (may be GroupConfig)"
msgstr ""
#: tiramisu/config.py:269
msgid "no option found in config with these criteria"
msgstr ""
#: tiramisu/config.py:532 tiramisu/option/optiondescription.py:72
msgid "option description seems to be part of an other config"
msgstr ""
#: tiramisu/config.py:790
msgid "cannot set leadership object has root optiondescription"
msgstr ""
#: tiramisu/config.py:792
msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr ""
#: tiramisu/config.py:840
msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr ""
#: tiramisu/config.py:1024
msgid "unknown config \"{}\""
msgstr ""
#: tiramisu/config.py:1047
msgid "child must be a Config, MixConfig or MetaConfig"
msgstr ""
#: tiramisu/config.py:1079
msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config"
msgstr ""
#: tiramisu/config.py:1085
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
#: tiramisu/config.py:1208
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
#: tiramisu/config.py:1246
msgid "config added has no name, the name is mandatory"
msgstr ""
#: tiramisu/config.py:1248
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr ""
#: tiramisu/config.py:1270
msgid "cannot find the config {}"
msgstr ""
#: tiramisu/config.py:1294
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
#: tiramisu/config.py:1303
msgid "child must be a Config or MetaConfig"
msgstr ""
#: tiramisu/config.py:1307
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/config.py:1319
msgid "metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/error.py:26
msgid "and"
msgstr ""
#: tiramisu/error.py:28
msgid "or"
msgstr ""
#: tiramisu/error.py:50
msgid " {} "
msgstr ""
#: tiramisu/error.py:98
msgid "property"
msgstr ""
#: tiramisu/error.py:100
msgid "properties"
msgstr ""
#: tiramisu/error.py:187
msgid "invalid value"
msgstr ""
#: tiramisu/error.py:192
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/error.py:208 tiramisu/error.py:212
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/function.py:113
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]})"
msgstr ""
#: tiramisu/function.py:115
msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]}) with netmask {netmask[\"value\"]} ({netmask[\"name\"]})"
msgstr ""
#: tiramisu/function.py:559
msgid "the value of \"{0}\" is {1}"
msgstr ""
#: tiramisu/function.py:561
msgid "the value of \"{0}\" is not {1}"
msgstr ""
#: tiramisu/option/baseoption.py:70 tiramisu/option/symlinkoption.py:40
msgid "\"{0}\" is an invalid name for an option"
msgstr ""
#: tiramisu/option/baseoption.py:83
msgid "invalid properties type {0} for {1}, must be a frozenset"
msgstr ""
#: tiramisu/option/baseoption.py:89
msgid "invalid property type {0} for {1}, must be a string or a Calculation"
msgstr ""
#: tiramisu/option/baseoption.py:227
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
#: tiramisu/option/baseoption.py:265
msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr ""
#: tiramisu/option/booloption.py:32
msgid "boolean"
msgstr ""
#: tiramisu/option/broadcastoption.py:33
msgid "broadcast address"
msgstr ""
#: tiramisu/option/broadcastoption.py:41
msgid "invalid string"
msgstr ""
#: tiramisu/option/choiceoption.py:38
msgid "choice"
msgstr ""
#: tiramisu/option/choiceoption.py:51
msgid "values must be a tuple or a calculation for {0}"
msgstr ""
#: tiramisu/option/choiceoption.py:67
msgid "the calculated values \"{0}\" for \"{1}\" is not a list"
msgstr ""
#: tiramisu/option/choiceoption.py:97
msgid "only \"{0}\" is allowed"
msgstr ""
#: tiramisu/option/choiceoption.py:99
msgid "only {0} are allowed"
msgstr ""
#: tiramisu/option/dateoption.py:33
msgid "date"
msgstr ""
#: tiramisu/option/domainnameoption.py:62
msgid "unknown type {0} for hostname"
msgstr ""
#: tiramisu/option/domainnameoption.py:65
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:67
msgid "allow_cidr_network must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:69
msgid "allow_without_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:71
msgid "allow_startswith_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:81
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:82
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:84
#: tiramisu/option/domainnameoption.py:85
msgid "could be a IP, otherwise {}"
msgstr ""
#: tiramisu/option/domainnameoption.py:125
msgid "invalid length (min 1)"
msgstr ""
#: tiramisu/option/domainnameoption.py:127
msgid "invalid length (max {0})"
msgstr ""
#: tiramisu/option/domainnameoption.py:133
msgid "must have dot"
msgstr ""
#: tiramisu/option/domainnameoption.py:135
msgid "invalid length (max 255)"
msgstr ""
#: tiramisu/option/domainnameoption.py:154
msgid "must not be an IP"
msgstr ""
#: tiramisu/option/domainnameoption.py:180
msgid "some characters are uppercase"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:65
msgid "suffixes in dynoptiondescription has to be a calculation"
msgstr ""
#: tiramisu/option/dynoptiondescription.py:109
msgid "invalid suffix \"{}\" for option \"{}\""
msgstr ""
#: tiramisu/option/emailoption.py:34
msgid "email address"
msgstr ""
#: tiramisu/option/filenameoption.py:31
msgid "file name"
msgstr ""
#: tiramisu/option/filenameoption.py:38
msgid "must starts with \"/\""
msgstr ""
#: tiramisu/option/floatoption.py:32
msgid "float"
msgstr ""
#: tiramisu/option/ipoption.py:33
msgid "IP"
msgstr ""
#: tiramisu/option/ipoption.py:57
msgid "it's in fact a network address"
msgstr ""
#: tiramisu/option/ipoption.py:59
msgid "it's in fact a broacast address"
msgstr ""
#: tiramisu/option/ipoption.py:72
msgid "CIDR address must have a \"/\""
msgstr ""
#: tiramisu/option/ipoption.py:83
msgid "shouldn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:85
msgid "mustn't be reserved IP"
msgstr ""
#: tiramisu/option/ipoption.py:89
msgid "should be private IP"
msgstr ""
#: tiramisu/option/ipoption.py:91
msgid "must be private IP"
msgstr ""
#: tiramisu/option/leadership.py:56
msgid "a leader and a follower are mandatories in leadership \"{}\""
msgstr ""
#: tiramisu/option/leadership.py:73
msgid "leader cannot have \"{}\" property"
msgstr ""
#: tiramisu/option/leadership.py:77
msgid "leadership \"{0}\" shall not have a symlinkoption"
msgstr ""
#: tiramisu/option/leadership.py:80
msgid "leadership \"{0}\" shall not have a subgroup"
msgstr ""
#: tiramisu/option/leadership.py:83
msgid "only multi option allowed in leadership \"{0}\" but option \"{1}\" is not a multi"
msgstr ""
#: tiramisu/option/macoption.py:34
msgid "mac address"
msgstr ""
#: tiramisu/option/netmaskoption.py:32
msgid "netmask address"
msgstr ""
#: tiramisu/option/networkoption.py:32
msgid "network address"
msgstr ""
#: tiramisu/option/networkoption.py:51
msgid "must use CIDR notation"
msgstr ""
#: tiramisu/option/networkoption.py:68
msgid "shouldn't be reserved network"
msgstr ""
#: tiramisu/option/networkoption.py:70
msgid "mustn't be reserved network"
msgstr ""
#: tiramisu/option/option.py:69
msgid "default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option/option.py:86
msgid "invalid multi type \"{}\" for \"{}\""
msgstr ""
#: tiramisu/option/option.py:102
msgid "validators must be a Calculation for \"{}\""
msgstr ""
#: tiramisu/option/option.py:127
msgid "invalid default_multi value \"{0}\" for option \"{1}\""
msgstr ""
#: tiramisu/option/option.py:137
msgid "invalid default_multi value \"{0}\" for option \"{1}\", must be a list for a submulti"
msgstr ""
#: tiramisu/option/option.py:291
msgid "the value \"{}\" is not unique"
msgstr ""
#: tiramisu/option/option.py:331
msgid "which must not be a list"
msgstr ""
#: tiramisu/option/option.py:373 tiramisu/option/option.py:399
msgid "which must be a list"
msgstr ""
#: tiramisu/option/option.py:392
msgid "which \"{}\" must be a list of list"
msgstr ""
#: tiramisu/option/optiondescription.py:109
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option/optiondescription.py:306
msgid "children in optiondescription \"{}\" must be a list"
msgstr ""
#: tiramisu/option/optiondescription.py:329
msgid "duplicate option name: \"{0}\""
msgstr ""
#: tiramisu/option/optiondescription.py:374
msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr ""
#: tiramisu/option/optiondescription.py:378
msgid "group_type: {0} not allowed"
msgstr ""
#: tiramisu/option/passwordoption.py:32
msgid "password"
msgstr ""
#: tiramisu/option/permissionsoption.py:38
msgid "unix file permissions"
msgstr ""
#: tiramisu/option/permissionsoption.py:52
msgid "only 3 or 4 octal digits are allowed"
msgstr ""
#: tiramisu/option/permissionsoption.py:65
msgid "user"
msgstr ""
#: tiramisu/option/permissionsoption.py:66
#: tiramisu/option/permissionsoption.py:68
msgid "group"
msgstr ""
#: tiramisu/option/permissionsoption.py:69
msgid "other"
msgstr ""
#: tiramisu/option/permissionsoption.py:73
msgid "too weak"
msgstr ""
#: tiramisu/option/portoption.py:41
msgid "port"
msgstr ""
#: tiramisu/option/portoption.py:71
msgid "inconsistency in allowed range"
msgstr ""
#: tiramisu/option/portoption.py:76
msgid "max value is empty"
msgstr ""
#: tiramisu/option/portoption.py:91
msgid "range must have two values only"
msgstr ""
#: tiramisu/option/portoption.py:93
msgid "first port in range must be smaller than the second one"
msgstr ""
#: tiramisu/option/stroption.py:33
msgid "string"
msgstr ""
#: tiramisu/option/symlinkoption.py:44
msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr ""
#: tiramisu/option/symlinkoption.py:60
msgid "cannot set symlinkoption in a dynoptiondescription"
msgstr ""
#: tiramisu/option/urloption.py:39
msgid "URL"
msgstr ""
#: tiramisu/option/urloption.py:91
msgid "must start with http:// or https://"
msgstr ""
#: tiramisu/option/urloption.py:122
msgid "must ends with a valid resource name"
msgstr ""
#: tiramisu/option/usernameoption.py:35
msgid "unix username"
msgstr ""
#: tiramisu/option/usernameoption.py:42
msgid "unix groupname"
msgstr ""
#: tiramisu/setting.py:302
msgid "can't rebind {0}"
msgstr ""
#: tiramisu/setting.py:308
msgid "can't unbind {0}"
msgstr ""
#: tiramisu/setting.py:515
msgid "invalid property type {type(new_prop)} for {option_bag.option.impl_getname()} with {prop.function.__name__} function"
msgstr ""
#: tiramisu/setting.py:521
msgid "leader cannot have \"{new_prop}\" property"
msgstr ""
#: tiramisu/setting.py:591
msgid "leader cannot have \"{list(not_allowed_properties)}\" property"
msgstr ""
#: tiramisu/setting.py:595
msgid "a leader ({opt.impl_get_display_name()}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr ""
#: tiramisu/setting.py:626
msgid "permissive must be a frozenset"
msgstr ""
#: tiramisu/setting.py:635
msgid "cannot add those permissives: {0}"
msgstr ""
#: tiramisu/todict.py:352
msgid "option {} only works when remotable is not \"none\""
msgstr ""
#: tiramisu/todict.py:505
msgid "unable to transform tiramisu object to dict: {}"
msgstr ""
#: tiramisu/todict.py:816 tiramisu/todict.py:955
msgid "unknown form {}"
msgstr ""
#: tiramisu/todict.py:862
msgid "not in current area"
msgstr ""
#: tiramisu/todict.py:883
msgid "only multi option can have action \"add\", but \"{}\" is not a multi"
msgstr ""
#: tiramisu/todict.py:885
msgid "unknown action {}"
msgstr ""
#: tiramisu/value.py:506 tiramisu/value.py:722
msgid "set owner \"{0}\" is forbidden"
msgstr ""
#: tiramisu/value.py:636
msgid "index {index} is greater than the length {length} for option \"{option_bag.option.impl_get_display_name()}\""
msgstr ""
#: tiramisu/value.py:695
msgid "information's item not found: {0}"
msgstr ""

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"logger for tiramisu" "logger for tiramisu"
# Copyright (C) 2019-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2019-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -19,12 +19,12 @@ from logging import getLogger, DEBUG, StreamHandler, Formatter
import os import os
log = getLogger("tiramisu") log = getLogger('tiramisu')
if os.environ.get("TIRAMISU_DEBUG") == "True": # pragma: no cover if os.environ.get('TIRAMISU_DEBUG') == 'True': # pragma: no cover
log.setLevel(DEBUG) log.setLevel(DEBUG)
handler = StreamHandler() handler = StreamHandler()
handler.setLevel(DEBUG) handler.setLevel(DEBUG)
formatter = Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter) handler.setFormatter(formatter)
log.addHandler(handler) log.addHandler(handler)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2014-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2014-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,14 +18,15 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""all official option""" """all official option
"""
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .dynoptiondescription import DynOptionDescription from .dynoptiondescription import DynOptionDescription
from .syndynoptiondescription import SynDynOptionDescription, SynDynLeadership
from .leadership import Leadership from .leadership import Leadership
from .baseoption import submulti from .baseoption import submulti
from .symlinkoption import SymLinkOption from .symlinkoption import SymLinkOption
from .syndynoption import SynDynOption
# from .syndynoption import SynDynOption, SynDynOptionDescription, SynDynLeadership
from .option import Option from .option import Option
from .choiceoption import ChoiceOption from .choiceoption import ChoiceOption
from .booloption import BoolOption from .booloption import BoolOption
@ -48,33 +49,11 @@ from .macoption import MACOption
from .permissionsoption import PermissionsOption from .permissionsoption import PermissionsOption
__all__ = ( __all__ = ('Leadership', 'OptionDescription', 'DynOptionDescription',
"Leadership", 'SynDynOptionDescription', 'SynDynLeadership', 'Option', 'SymLinkOption',
"OptionDescription", 'SynDynOption', 'ChoiceOption', 'BoolOption', 'DateOption',
"DynOptionDescription", 'IntOption', 'FloatOption', 'StrOption',
# 'SynDynOptionDescription', 'SynDynLeadership','SynDynOption', 'IPOption', 'PortOption', 'NetworkOption', 'NetmaskOption',
"Option", 'BroadcastOption', 'DomainnameOption', 'EmailOption', 'URLOption',
"SymLinkOption", 'UsernameOption', 'GroupnameOption', 'FilenameOption', 'PasswordOption', 'submulti',
"ChoiceOption", 'RegexpOption', 'MACOption', 'PermissionsOption')
"BoolOption",
"DateOption",
"IntOption",
"FloatOption",
"StrOption",
"IPOption",
"PortOption",
"NetworkOption",
"NetmaskOption",
"BroadcastOption",
"DomainnameOption",
"EmailOption",
"URLOption",
"UsernameOption",
"GroupnameOption",
"FilenameOption",
"PasswordOption",
"submulti",
"RegexpOption",
"MACOption",
"PermissionsOption",
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2014-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2014-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,21 +18,16 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""base option""" """base option
from typing import FrozenSet, Set, Any, List, Optional, Dict """
from typing import FrozenSet, Set, Any, List
import weakref import weakref
from itertools import chain from itertools import chain
from ..i18n import _ from ..i18n import _
from ..setting import undefined from ..setting import undefined
from ..autolib import ( from ..autolib import Calculation, ParamOption
Calculation,
ParamOption,
ParamSelfOption,
ParamInformation,
ParamSelfInformation,
)
STATIC_TUPLE = frozenset() STATIC_TUPLE = frozenset()
@ -41,42 +36,36 @@ submulti = 2
def valid_name(name): def valid_name(name):
"""valid option name""" """valid option name
"""
if not isinstance(name, str): if not isinstance(name, str):
return False return False
if "." in name: if '.' in name:
return False return False
return True return True
# ____________________________________________________________ #____________________________________________________________
# #
class Base: class Base:
"""Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...)""" """Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...)
"""
__slots__ = ('_name',
'_path',
'_informations',
'_subdyns',
'_properties',
'_has_dependency',
'_dependencies',
'_suffixes_dependencies',
'__weakref__'
)
__slots__ = ( def __init__(self,
"_name", name: str,
"_path", doc: str,
"_informations", properties=None,
"_subdyns", is_multi: bool=False) -> None:
"_properties",
"_has_dependency",
"_dependencies",
"_dependencies_information",
"_identifiers_dependencies",
"could_conflict",
"__weakref__",
)
def __init__(
self,
name: str,
doc: str,
informations: Optional[Dict],
*,
properties=None,
is_multi: bool = False,
) -> None:
if not valid_name(name): if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(name)) raise ValueError(_('"{0}" is an invalid name for an option').format(name))
if properties is None: if properties is None:
@ -87,101 +76,77 @@ class Base:
# if option is a multi, it cannot be 'empty' (None not allowed in the list) # if option is a multi, it cannot be 'empty' (None not allowed in the list)
# and cannot have multiple time the same value # and cannot have multiple time the same value
# 'empty' and 'unique' are removed for follower's option # 'empty' and 'unique' are removed for follower's option
if "notunique" not in properties: if 'notunique' not in properties:
properties = properties | {"unique"} properties = properties | {'unique'}
if "notempty" not in properties: if 'notempty' not in properties:
properties = properties | {"empty"} properties = properties | {'empty'}
assert isinstance(properties, frozenset), _( assert isinstance(properties, frozenset), _('invalid properties type {0} for {1},'
"invalid properties type {0} for {1}," " must be a frozenset" ' must be a frozenset').format(type(properties),
).format(type(properties), name) name)
if not self.impl_is_optiondescription() and "novalidator" not in properties:
properties = properties | {"validator"}
_setattr = object.__setattr__
_setattr(self, "_name", name)
_setattr(self, "_informations", {"doc": doc})
_setattr(self, "could_conflict", [])
for prop in properties: for prop in properties:
if not isinstance(prop, str): if not isinstance(prop, str):
if not isinstance(prop, Calculation): if not isinstance(prop, Calculation):
raise ValueError( raise ValueError(_('invalid property type {0} for {1}, must be a string or a '
_( 'Calculation').format(type(prop), name))
"invalid property type {0} for {1}, must be a string or a " for param in chain(prop.params.args, prop.params.kwargs.values()):
"Calculation" if isinstance(param, ParamOption):
).format(type(prop), name) param.option._add_dependency(self)
) _setattr = object.__setattr__
self.value_dependency(prop, type_="property") _setattr(self, '_name', name)
_setattr(self, '_informations', {'doc': doc})
if properties: if properties:
_setattr(self, "_properties", properties) _setattr(self, '_properties', properties)
self.set_informations(informations)
def set_informations( def impl_has_dependency(self,
self, self_is_dep: bool=True,
informations: Optional[Dict], ) -> bool:
) -> None: """this has dependency
if not informations: """
return
for key, value in informations.items():
self._set_information(
key,
value,
)
def impl_has_dependency(
self,
self_is_dep: bool = True,
) -> bool:
"""this has dependency"""
if self_is_dep is True: if self_is_dep is True:
return getattr(self, "_has_dependency", False) return getattr(self, '_has_dependency', False)
return hasattr(self, "_dependencies") return hasattr(self, '_dependencies')
def get_dependencies( def get_dependencies(self,
self, context_od,
context_od, ) -> Set[str]:
) -> Set[str]: ret = set(getattr(self, '_dependencies', STATIC_TUPLE))
ret = set(getattr(self, "_dependencies", STATIC_TUPLE)) if context_od and hasattr(context_od, '_dependencies'):
if context_od and hasattr(context_od, "_dependencies"):
# add options that have context is set in calculation # add options that have context is set in calculation
return ( return set(context_od._dependencies) | ret # pylint: disable=protected-access
set(context_od._dependencies) | ret
) # pylint: disable=protected-access
return ret return ret
def _get_identifiers_dependencies(self) -> Set[str]: def _get_suffixes_dependencies(self) -> Set[str]:
return getattr(self, "_identifiers_dependencies", STATIC_TUPLE) return getattr(self, '_suffixes_dependencies', STATIC_TUPLE)
def _add_dependency( def _add_dependency(self,
self, option,
option, is_suffix: bool=False,
type_, ) -> None:
is_identifier: bool = False,
) -> None:
woption = weakref.ref(option) woption = weakref.ref(option)
options = self.get_dependencies(None) options = self.get_dependencies(None)
options.add((type_ == "default", woption)) options.add(woption)
self._dependencies = tuple( self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
options if is_suffix:
) # pylint: disable=attribute-defined-outside-init options = list(self._get_suffixes_dependencies())
if is_identifier:
options = list(self._get_identifiers_dependencies())
options.append(woption) options.append(woption)
self._identifiers_dependencies = tuple( self._suffixes_dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
options
) # pylint: disable=attribute-defined-outside-init
def impl_is_optiondescription(self) -> bool: def impl_is_optiondescription(self) -> bool:
"""option is an option description""" """option is an option description
"""
return False return False
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
"""option is not a dyn option description""" """option is not a dyn option description
"""
return False return False
def impl_is_sub_dyn_optiondescription(self): def impl_is_sub_dyn_optiondescription(self):
return False return False
def impl_getname(self) -> str: def impl_getname(self) -> str:
"""get name""" """get name
"""
return self._name # pylint: disable=no-member return self._name # pylint: disable=no-member
def _set_readonly(self) -> None: def _set_readonly(self) -> None:
@ -190,40 +155,40 @@ class Base:
dico = self._informations # pylint: disable=no-member dico = self._informations # pylint: disable=no-member
keys = tuple(dico.keys()) keys = tuple(dico.keys())
if len(keys) == 1: if len(keys) == 1:
dico = dico["doc"] dico = dico['doc']
else: else:
dico = tuple([keys, tuple(dico.values())]) dico = tuple([keys, tuple(dico.values())])
_setattr(self, "_informations", dico) _setattr(self, '_informations', dico)
_setattr(self, "could_conflict", tuple(self.could_conflict)) extra = getattr(self, '_extra', None)
extra = getattr(self, "_extra", None)
if extra is not None: if extra is not None:
_setattr( _setattr(self, '_extra', tuple([tuple(extra.keys()), tuple(extra.values())]))
self, "_extra", tuple([tuple(extra.keys()), tuple(extra.values())])
)
def impl_is_readonly(self) -> str: def impl_is_readonly(self) -> str:
"""the option is readonly""" """the option is readonly
return hasattr(self, "_path") """
return hasattr(self, '_path')
def impl_getproperties(self) -> FrozenSet[str]: def impl_getproperties(self) -> FrozenSet[str]:
"""get properties""" """get properties
return getattr(self, "_properties", frozenset()) """
return getattr(self, '_properties', frozenset())
def _setsubdyn( def _setsubdyn(self,
self, subdyn,
subdyn, ) -> None:
) -> None:
# pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
if getattr(self, "_subdyns", None) is None: if getattr(self, '_subdyns', None) is None:
self._subdyns = [] self._subdyns = []
self._subdyns.append(subdyn) self._subdyns.append(subdyn)
def issubdyn(self) -> bool: def issubdyn(self) -> bool:
"""is sub dynoption""" """is sub dynoption
return getattr(self, "_subdyns", None) is not None """
return getattr(self, '_subdyns', None) is not None
def getsubdyn(self): def getsubdyn(self):
"""get sub dynoption""" """get sub dynoption
"""
return self._subdyns[0]() return self._subdyns[0]()
def get_sub_dyns(self): def get_sub_dyns(self):
@ -231,12 +196,10 @@ class Base:
# ____________________________________________________________ # ____________________________________________________________
# information # information
def _get_information( def impl_get_information(self,
self, key: str,
subconfig: "SubConfig", default: Any=undefined,
key: str, ) -> Any:
default: Any = undefined,
) -> Any:
"""retrieves one information's item """retrieves one information's item
:param key: the item string (ex: "help") :param key: the item string (ex: "help")
@ -246,7 +209,7 @@ class Base:
if key in dico[0]: if key in dico[0]:
return dico[1][dico[0].index(key)] return dico[1][dico[0].index(key)]
elif isinstance(dico, str): elif isinstance(dico, str):
if key == "doc": if key == 'doc':
return dico return dico
elif isinstance(dico, dict): elif isinstance(dico, dict):
if key in dico: if key in dico:
@ -254,17 +217,13 @@ class Base:
if default is not undefined: if default is not undefined:
return default return default
# pylint: disable=no-member # pylint: disable=no-member
raise ValueError( raise ValueError(_(f'information\'s item for "{self.impl_get_display_name()}" '
_('information\'s item for {0} not found: "{1}"').format( f'not found: "{key}"'))
self.impl_get_display_name(subconfig, with_quote=True), key
)
)
def _set_information( def impl_set_information(self,
self, key: str,
key: str, value: Any,
value: Any, ) -> None:
) -> None:
"""updates the information's attribute """updates the information's attribute
(which is a dictionary) (which is a dictionary)
@ -272,20 +231,20 @@ class Base:
:param value: information's value (ex: "the help string") :param value: information's value (ex: "the help string")
""" """
if self.impl_is_readonly(): if self.impl_is_readonly():
raise AttributeError( raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
_("'{0}' ({1}) object attribute '{2}' is" " read-only").format( " read-only").format(self.__class__.__name__,
self.__class__.__name__, self, key self,
) key))
)
self._informations[key] = value # pylint: disable=no-member self._informations[key] = value # pylint: disable=no-member
def _list_information(self) -> Any: def impl_list_information(self) -> Any:
"""get the list of information keys""" """get the list of information keys
"""
dico = self._informations # pylint: disable=no-member dico = self._informations # pylint: disable=no-member
if isinstance(dico, tuple): if isinstance(dico, tuple):
return list(dico[0]) return list(dico[0])
if not isinstance(dico, dict): if isinstance(dico, str):
return ["doc"] return ['doc']
# it's a dict # it's a dict
return list(dico.keys()) return list(dico.keys())
@ -295,14 +254,12 @@ class BaseOption(Base):
in options that have to be set only once, it is of course done in the in options that have to be set only once, it is of course done in the
__setattr__ method __setattr__ method
""" """
__slots__ = ('_display_name_function',)
__slots__ = ("_display_name_function",) def __setattr__(self,
name: str,
def __setattr__( value: Any,
self, ) -> Any:
name: str,
value: Any,
) -> Any:
"""set once and only once some attributes in the option, """set once and only once some attributes in the option,
like `_name`. `_name` cannot be changed once the option is like `_name`. `_name` cannot be changed once the option is
pushed in the :class:`tiramisu.option.OptionDescription`. pushed in the :class:`tiramisu.option.OptionDescription`.
@ -313,118 +270,85 @@ class BaseOption(Base):
""" """
# never change _name in an option or attribute when object is readonly # never change _name in an option or attribute when object is readonly
if self.impl_is_readonly(): if self.impl_is_readonly():
raise AttributeError( raise AttributeError(_('"{}" ({}) object attribute "{}" is'
_('"{}" ({}) object attribute "{}" is' " read-only").format( ' read-only').format(self.__class__.__name__,
self.__class__.__name__, self.impl_get_display_name(None), name self.impl_get_display_name(),
) name))
)
super().__setattr__(name, value) super().__setattr__(name, value)
def impl_getpath(self) -> str: def impl_getpath(self) -> str:
"""get the path of the option""" """get the path of the option
"""
try: try:
return self._path return self._path
except AttributeError as err: except AttributeError as err:
raise AttributeError( raise AttributeError(_(f'"{self.impl_get_display_name()}" not part of any Config')) \
_("{0} not part of any Config").format( from err
self.impl_get_display_name(None, with_quote=True)
)
) from err
def impl_get_display_name( def impl_get_display_name(self,
self, dynopt=None,
subconfig: "SubConfig", ) -> str:
*, """get display name
with_quote: bool = False, """
) -> str: if dynopt is None:
"""get display name""" dynopt = self
if hasattr(self, "_display_name_function"): if hasattr(self, '_display_name_function'):
return self._display_name_function( return self._display_name_function(dynopt)
self, name = self.impl_get_information('doc', None)
subconfig, if name is None or name == '':
with_quote=with_quote, name = dynopt.impl_getname()
)
name = self._get_information(subconfig, "doc", None)
if name is None or name == "":
if subconfig and subconfig.path:
name = subconfig.path.rsplit(".", 1)[-1]
else:
name = self._name
if with_quote:
return f'"{name}"'
return name return name
def reset_cache( def reset_cache(self,
self, path: str,
path: str, config_bag: 'OptionBag',
config_bag: "OptionBag", resetted_opts: List[Base], # pylint: disable=unused-argument
resetted_opts: List[Base], # pylint: disable=unused-argument ) -> None:
) -> None: """reset cache
"""reset cache""" """
context = config_bag.context context = config_bag.context
context.properties_cache.delcache(path) context.properties_cache.delcache(path)
context._impl_permissives_cache.delcache( context._impl_permissives_cache.delcache(path) # pylint: disable=protected-access
path
) # pylint: disable=protected-access
if not self.impl_is_optiondescription(): if not self.impl_is_optiondescription():
context.get_values_cache().delcache( context.get_values_cache().delcache(path) # pylint: disable=protected-access
path
) # pylint: disable=protected-access
def impl_is_symlinkoption(self) -> bool: def impl_is_symlinkoption(self) -> bool:
"""the option is not a symlinkoption""" """the option is not a symlinkoption
"""
return False return False
def get_dependencies_information(self) -> List[str]: def get_dependencies_information(self) -> List[str]:
"""get dependencies information""" """get dependencies information
return getattr(self, "_dependencies_information", {}) """
return getattr(self, '_dependencies_information', {})
def value_dependencies( def to_sub_dyoption(self,
self, suffixes: list[str],
value: Any, ):
is_identifier: bool = False, sub_dyn = self
) -> Any: # retrieve all subdyn options
"""parse dependancies to add dependencies""" sub_dyns = []
if isinstance(value, list): while True:
for val in value: sub_dyn = sub_dyn.getsubdyn()
if isinstance(value, list): sub_dyns.append(sub_dyn)
self.value_dependencies(val, is_identifier) if not sub_dyn.issubdyn():
elif isinstance(value, Calculation): break
self.value_dependency(val, is_identifier) paths = []
elif isinstance(value, Calculation): parent_path = self.impl_getpath().rsplit('.', 1)[0]
self.value_dependency(value, is_identifier) suffix_idx = len(sub_dyns) - 1
for sub_dyn in sub_dyns:
def value_dependency( dyn_path = sub_dyn.impl_getpath()
self, value: Any, is_identifier: bool = False, type_: str = "default" if dyn_path.count('.') == parent_path.count('.'):
) -> Any: *root_paths, dyn_path_ = parent_path.split('.', dyn_path.count('.') + 1)
if not isinstance(is_identifier, bool): else:
raise Exception() *root_paths, dyn_path_, child_path = parent_path.split('.', dyn_path.count('.') + 1)
"""parse dependancy to add dependencies""" paths.insert(0, child_path)
for param in chain(value.params.args, value.params.kwargs.values()): paths.insert(0, sub_dyn.impl_getname(suffixes[suffix_idx]))
if isinstance(param, ParamOption): suffix_idx -= 1
# pylint: disable=protected-access parent_path = '.'.join(root_paths)
if is_identifier: if parent_path:
_type_ = "identifier" paths.insert(0, parent_path)
else: full_parent_path = '.'.join(paths)
_type_ = type_ return self.to_dynoption(full_parent_path,
param.option._add_dependency(self, _type_, is_identifier=is_identifier) suffixes,
self._has_dependency = True )
elif isinstance(param, ParamSelfOption) and not param.dynamic:
self._add_dependency(self, "self")
self._has_dependency = True
elif isinstance(param, ParamInformation):
dest = self
if isinstance(param, ParamSelfInformation):
opt = weakref.ref(self)
elif param.option:
dest = param.option
opt = weakref.ref(self)
else:
param.set_self_option(self)
opt = None
if not getattr(dest, "_dependencies_information", {}):
dest._dependencies_information = {None: []}
dest._dependencies_information[None].append(param)
dest._dependencies_information.setdefault(
param.information_name, []
).append(opt)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,23 +18,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
# ____________________________________________________________ # ____________________________________________________________
"""BoolOption""" """BoolOption
"""
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
class BoolOption(Option): class BoolOption(Option):
"""represents a choice between ``True`` and ``False``""" """represents a choice between ``True`` and ``False``
"""
__slots__ = tuple() __slots__ = tuple()
_type = "boolean" _type = _('boolean')
_t_type = _("boolean")
def validate( def validate(self,
self, value: bool,
value: bool, ) -> None:
) -> None: """validate value
"""validate value""" """
if not isinstance(value, bool): if not isinstance(value, bool):
raise ValueError() raise ValueError()

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,26 +18,32 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""BroadcastOption""" """BroadcastOption
"""
from ipaddress import ip_address from ipaddress import ip_address
from ..i18n import _ from ..i18n import _
from .stroption import StrOption from .option import Option
class BroadcastOption(StrOption): class BroadcastOption(Option):
"""represents the choice of a broadcast""" """represents the choice of a broadcast
"""
__slots__ = tuple() __slots__ = tuple()
_type = "broadcast address" _type = _('broadcast address')
_t_type = _("broadcast address")
def validate( def validate(self,
self, value: str,
value: str, ) -> None:
) -> None: """validate
"""validate""" """
super().validate(value) if not isinstance(value, str):
raise ValueError(_('invalid string'))
if value.count('.') != 3:
raise ValueError()
for val in value.split('.'):
if val.startswith("0") and len(val) > 1:
raise ValueError()
try: try:
ip_address(value) ip_address(value)
except ValueError as err: except ValueError as err:

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,14 +18,14 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""ChoiceOption""" """ChoiceOption
"""
from typing import Any from typing import Any
from itertools import chain
from ..setting import undefined from ..setting import undefined, OptionBag
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
from ..autolib import Calculation, get_calculated_value, ParamOption from ..autolib import Calculation
from ..error import ConfigError, display_list from ..error import ConfigError, display_list
@ -34,77 +34,67 @@ class ChoiceOption(Option):
The option can also have the value ``None`` The option can also have the value ``None``
""" """
__slots__ = tuple() __slots__ = tuple()
_type = "choice" _type = _('choice')
_t_type = _("choice")
def __init__(self,
name,
doc,
values,
*args,
**kwargs):
def __init__(self, name, doc, values, *args, **kwargs):
""" """
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
if isinstance(values, Calculation): if not isinstance(values, (Calculation, tuple)):
self.value_dependency(values, type_="choice") raise TypeError(_('values must be a tuple or a calculation for {0}'
elif not isinstance(values, tuple): ).format(name))
raise TypeError(
_("values must be a tuple or a calculation for {0}").format(name)
)
self._choice_values = values self._choice_values = values
super().__init__(name, doc, *args, **kwargs) super().__init__(name,
doc,
*args,
**kwargs)
def impl_get_values( def impl_get_values(self,
self, option_bag: OptionBag,
subconfig: "SubConfig", ):
uncalculated: bool = False, """get values allowed by option
): """
"""get values allowed by option""" if isinstance(self._choice_values, Calculation):
choices = self._choice_values values = self._choice_values.execute(option_bag)
if isinstance(choices, tuple): if values is not undefined and not isinstance(values, list):
choices = list(choices) raise ConfigError(_('the calculated values "{0}" for "{1}" is not a list'
if uncalculated: '').format(values, self.impl_getname()))
return choices else:
values = get_calculated_value( values = self._choice_values
subconfig,
choices,
)[0]
if values != undefined and not isinstance(values, (list, tuple)):
raise ConfigError(
_('the calculated values "{0}" for "{1}" is not a list' "").format(
values, self.impl_getname()
),
subconfig=subconfig,
)
return values return values
def validate( def validate(self,
self, value: Any,
value: Any, ) -> None:
) -> None: """nothing to valide
"""nothing to valide""" """
def validate_with_option( def validate_with_option(self,
self, value: Any,
value: Any, option_bag: OptionBag,
subconfig: "SubConfig", loaded: bool,
loaded: bool, ) -> None:
) -> None:
if loaded and isinstance(self._choice_values, Calculation): if loaded and isinstance(self._choice_values, Calculation):
return return
values = self.impl_get_values(subconfig) values = self.impl_get_values(option_bag)
self.validate_values(value, values) self.validate_values(value, values)
def validate_values( def validate_values(self,
self, value,
value, values,
values, ) -> None:
) -> None: """validate values
"""validate values""" """
if values is not undefined and value not in values: if values is not undefined and value not in values:
if len(values) == 1: if len(values) == 1:
raise ValueError(_('only "{0}" is allowed' "").format(values[0])) raise ValueError(_('only "{0}" is allowed'
raise ValueError( '').format(values[0]))
_("only {0} are allowed" "").format( raise ValueError(_('only {0} are allowed'
display_list(values, add_quote=True) '').format(display_list(values, add_quote=True)))
)
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""DateOption""" """DateOption
"""
from datetime import datetime from datetime import datetime
from ..i18n import _ from ..i18n import _
@ -26,13 +27,13 @@ from .stroption import StrOption
class DateOption(StrOption): class DateOption(StrOption):
"""represents the choice of a date""" """represents the choice of a date
"""
__slots__ = tuple() __slots__ = tuple()
_type = "date" _type = _('date')
_t_type = _("date")
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
try: try:
datetime.strptime(value, "%Y-%m-%d") datetime.strptime(value, "%Y-%m-%d")

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,9 +18,9 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""DomainnameOption""" """DomainnameOption
"""
import re import re
import socket
from ipaddress import ip_interface from ipaddress import ip_interface
from typing import Any, Optional, List from typing import Any, Optional, List
@ -39,165 +39,131 @@ class DomainnameOption(StrOption):
domainname: domainname:
fqdn: with tld, not supported yet fqdn: with tld, not supported yet
""" """
__slots__ = tuple() __slots__ = tuple()
_type = "domain name" _type = _('domain name')
_t_type = _("domain name")
def __init__( def __init__(self,
self, name: str,
name: str, doc: str,
doc: str, default: Any=undefined,
*args, default_multi: Any=None,
allow_ip: bool = False, multi: bool=False,
allow_cidr_network: bool = False, validators: Optional[List[Calculation]]=None,
type: str = "domainname", properties: Optional[List[str]]=None,
allow_without_dot: bool = False, warnings_only: bool=False,
allow_startswith_dot: bool = False, allow_ip: bool=False,
test_existence: bool = False, allow_cidr_network: bool=False,
_extra: dict = None, type: str='domainname',
**kwargs, allow_without_dot: bool=False,
) -> None: allow_startswith_dot: bool=False,
) -> None:
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments # pylint: disable=too-many-branches,too-many-locals,too-many-arguments
if _extra is None: if type not in ['netbios', 'hostname', 'domainname']:
extra = {} raise ValueError(_('unknown type {0} for hostname').format(type))
else: extra = {'_dom_type': type}
extra = _extra
if type not in ["netbios", "hostname", "domainname"]:
raise ValueError(_("unknown type {0} for hostname").format(type))
extra["type"] = type
if not isinstance(allow_ip, bool): if not isinstance(allow_ip, bool):
raise ValueError(_("allow_ip must be a boolean")) raise ValueError(_('allow_ip must be a boolean'))
if not isinstance(allow_cidr_network, bool): if not isinstance(allow_cidr_network, bool):
raise ValueError(_("allow_cidr_network must be a boolean")) raise ValueError(_('allow_cidr_network must be a boolean'))
if not isinstance(allow_without_dot, bool): if not isinstance(allow_without_dot, bool):
raise ValueError(_("allow_without_dot must be a boolean")) raise ValueError(_('allow_without_dot must be a boolean'))
if not isinstance(allow_startswith_dot, bool): if not isinstance(allow_startswith_dot, bool):
raise ValueError(_("allow_startswith_dot must be a boolean")) raise ValueError(_('allow_startswith_dot must be a boolean'))
extra["allow_without_dot"] = allow_without_dot extra['_allow_without_dot'] = allow_without_dot
extra["test_existence"] = test_existence if type == 'domainname':
if type == "domainname":
if allow_without_dot: if allow_without_dot:
min_time = 0 min_time = 0
else: else:
min_time = 1 min_time = 1
regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format( regexp = r'((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}'.format(self._get_len(type), min_time)
self._get_len(type), min_time
)
msg = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
)
else: else:
regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type)) regexp = r'((?!-)[a-z0-9-]{{1,{0}}})'.format(self._get_len(type))
msg = _( msg = _('must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed')
'must start with lowercase characters followed by lowercase characters, number and "-" characters are allowed' msg_warning = _('must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded')
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number and "-" characters are recommanded'
)
if allow_ip: if allow_ip:
msg = _("could be a IP, otherwise {}").format(msg) msg = _('could be a IP, otherwise {}').format(msg)
msg_warning = _("could be a IP, otherwise {}").format(msg_warning) msg_warning = _('could be a IP, otherwise {}').format(msg_warning)
if not allow_cidr_network: if not allow_cidr_network:
regexp = r"(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))".format( regexp = r'(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))'.format(regexp)
regexp
)
else: else:
regexp = r"(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))".format( regexp = r'(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))'.format(regexp)
regexp regexp = r'^{0}$'.format(regexp)
) extra['_domain_re'] = re.compile(regexp)
regexp = r"^{0}$".format(regexp) extra['_domain_re_message'] = msg
extra["_domain_re"] = re.compile(regexp) extra['_domain_re_message_warning'] = msg_warning
extra["_domain_re_message"] = msg extra['_has_upper'] = re.compile('[A-Z]')
extra["_domain_re_message_warning"] = msg_warning
extra["_has_upper"] = re.compile("[A-Z]")
if allow_ip: if allow_ip:
extra["_ip"] = IPOption( extra['_ip'] = IPOption(name,
name, doc)
doc, extra['_allow_ip'] = allow_ip
)
extra["allow_ip"] = allow_ip
if allow_cidr_network: if allow_cidr_network:
extra["_network"] = NetworkOption( extra['_network'] = NetworkOption(name,
name, doc,
doc, cidr=True)
cidr=True, extra['_allow_cidr_network'] = allow_cidr_network
) extra['_allow_startswith_dot'] = allow_startswith_dot
extra["allow_cidr_network"] = allow_cidr_network
extra["allow_startswith_dot"] = allow_startswith_dot
super().__init__( super().__init__(name,
name, doc,
doc, default=default,
*args, default_multi=default_multi,
extra=extra, multi=multi,
**kwargs, validators=validators,
) properties=properties,
warnings_only=warnings_only,
extra=extra)
def _get_len(self, type_): def _get_len(self, type_):
if type_ == "netbios": if type_ == 'netbios':
return 15 return 15
return 63 return 63
def _validate_domain(self, value: str) -> None: def _validate_domain(self,
value: str) -> None:
def _valid_length(val): def _valid_length(val):
if len(val) < 1: if len(val) < 1:
raise ValueError(_("invalid length (min 1)")) raise ValueError(_("invalid length (min 1)"))
if len(val) > part_name_length: if len(val) > part_name_length:
raise ValueError( raise ValueError(_("invalid length (max {0})"
_("invalid length (max {0})" "").format(part_name_length) "").format(part_name_length))
)
part_name_length = self._get_len(self.impl_get_extra("type")) part_name_length = self._get_len(self.impl_get_extra('_dom_type'))
if self.impl_get_extra("type") == "domainname": if self.impl_get_extra('_dom_type') == 'domainname':
if not self.impl_get_extra("allow_without_dot") and not "." in value: if not self.impl_get_extra('_allow_without_dot') and not "." in value:
raise ValueError(_("must have dot")) raise ValueError(_("must have dot"))
if len(value) > 255: if len(value) > 255:
raise ValueError(_("invalid length (max 255)")) raise ValueError(_("invalid length (max 255)"))
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."): if self.impl_get_extra('_allow_startswith_dot') and value.startswith('.'):
val = value[1:] val = value[1:]
else: else:
val = value val = value
if val.endswith("."): if val.endswith('.'):
nval = val[:-1] nval = val[:-1]
else: else:
nval = val nval = val
for dom in nval.split("."): for dom in nval.split('.'):
_valid_length(dom) _valid_length(dom)
else: else:
_valid_length(value) _valid_length(value)
self._validate_domain_resolution(value)
def _validate_domain_resolution(self, value: str) -> None: def _validate_ip_network(self,
if not value.startswith(".") and self.impl_get_extra("test_existence") is True: value: str) -> None:
try: allow_ip = self.impl_get_extra('_allow_ip')
socket.gethostbyname(value) allow_cidr_network = self.impl_get_extra('_allow_cidr_network')
except socket.gaierror as err:
raise ValueError(_("DNS resolution failed").format(value)) from err
except Exception as err:
raise ValueError(
_("error resolving DNS: {1}").format(value, err)
) from err
def _validate_ip_network(self, value: str) -> None:
allow_ip = self.impl_get_extra("allow_ip")
allow_cidr_network = self.impl_get_extra("allow_cidr_network")
if allow_ip is False and allow_cidr_network is False: if allow_ip is False and allow_cidr_network is False:
raise ValueError(_("must not be an IP")) raise ValueError(_('must not be an IP'))
if allow_ip is True: if allow_ip is True:
try: try:
self.impl_get_extra("_ip").validate(value) self.impl_get_extra('_ip').validate(value)
return return
except ValueError as err: except ValueError as err:
if allow_cidr_network is False: if allow_cidr_network is False:
raise err raise err
if allow_cidr_network is True: if allow_cidr_network is True:
self.impl_get_extra("_network").validate(value) self.impl_get_extra('_network').validate(value)
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
try: try:
# check if it's an IP or network # check if it's an IP or network
@ -207,40 +173,42 @@ class DomainnameOption(StrOption):
else: else:
self._validate_ip_network(value) self._validate_ip_network(value)
def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None: def _second_level_validation_domain(self,
if self.impl_get_extra("_has_upper").search(value): value: str,
raise ValueError(_("some characters are uppercase")) warnings_only: bool) -> None:
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."): if self.impl_get_extra('_has_upper').search(value):
raise ValueError(_('some characters are uppercase'))
if self.impl_get_extra('_allow_startswith_dot') and value.startswith('.'):
val = value[1:] val = value[1:]
else: else:
val = value val = value
if val.endswith("."): if val.endswith('.'):
nval = val[:-1] nval = val[:-1]
else: else:
nval = val nval = val
if not self.impl_get_extra("_domain_re").search(nval): if not self.impl_get_extra('_domain_re').search(nval):
if warnings_only: if warnings_only:
raise ValueError(self.impl_get_extra("_domain_re_message_warning")) raise ValueError(self.impl_get_extra('_domain_re_message_warning'))
raise ValueError(self.impl_get_extra("_domain_re_message")) raise ValueError(self.impl_get_extra('_domain_re_message'))
def _second_level_validation_ip_network( def _second_level_validation_ip_network(self,
self, value: str, warnings_only: bool value: str,
) -> None: warnings_only: bool) -> None:
allow_ip = self.impl_get_extra("allow_ip") allow_ip = self.impl_get_extra('_allow_ip')
allow_cidr_network = self.impl_get_extra("allow_cidr_network") allow_cidr_network = self.impl_get_extra('_allow_cidr_network')
# it's an IP so validate with IPOption # it's an IP so validate with IPOption
if allow_ip is True and allow_cidr_network is False: if allow_ip is True and allow_cidr_network is False:
try: try:
self.impl_get_extra("_ip").second_level_validation(value, warnings_only) self.impl_get_extra('_ip').second_level_validation(value, warnings_only)
return return
except ValueError as err: except ValueError as err:
raise err raise err
if allow_cidr_network is True: if allow_cidr_network is True:
self.impl_get_extra("_network").second_level_validation( self.impl_get_extra('_network').second_level_validation(value, warnings_only)
value, warnings_only
)
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
try: try:
# check if it's an IP or network # check if it's an IP or network
ip_interface(value) ip_interface(value)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,149 +18,185 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""DynOptionDescription""" """DynOptionDescription
"""
import re import re
import weakref import weakref
from typing import List, Any, Optional, Dict from typing import List, Any, Optional, Tuple
from itertools import chain from itertools import chain
from ..autolib import ParamOption from ..autolib import ParamOption
from ..i18n import _ from ..i18n import _
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynLeadership
from .baseoption import BaseOption from .baseoption import BaseOption
from ..setting import ConfigBag, undefined from ..setting import OptionBag, ConfigBag, undefined
from ..autolib import Calculation, get_calculated_value from ..error import ConfigError
from ..autolib import Calculation
NAME_REGEXP = re.compile(r"^[a-zA-Z\d\-_]*$") NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
class DynOptionDescription(OptionDescription): class DynOptionDescription(OptionDescription):
"""dyn option description""" """dyn option description
"""
__slots__ = ('_suffixes',
'_subdyns',
)
__slots__ = ( def __init__(self,
"_identifiers", name: str,
"_subdyns", doc: str,
) children: List[BaseOption],
suffixes: Calculation,
def __init__( properties=None,
self, ) -> None:
name: str,
doc: str,
children: List[BaseOption],
identifiers: Calculation,
**kwargs,
) -> None:
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
super().__init__( super().__init__(name,
name, doc,
doc, children,
children, properties,
**kwargs, )
)
# check children + set relation to this dynoptiondescription # check children + set relation to this dynoptiondescription
wself = weakref.ref(self) wself = weakref.ref(self)
for child in children: for child in children:
child._setsubdyn(wself) child._setsubdyn(wself)
# add identifiers # add suffixes
self.value_dependencies(identifiers, is_identifier=True) if __debug__ and not isinstance(suffixes, Calculation):
self._identifiers = identifiers raise ConfigError(_('suffixes in dynoptiondescription has to be a calculation'))
for param in chain(suffixes.params.args, suffixes.params.kwargs.values()):
if isinstance(param, ParamOption):
param.option._add_dependency(self,
is_suffix=True,
)
self._suffixes = suffixes
def convert_identifier_to_path( def convert_suffix_to_path(self,
self, suffix: Any,
identifier: Any, ) -> str:
) -> str: """convert suffix to use it to a path
"""convert identifier to use it to a path""" """
if identifier is None: if suffix is None:
return None return None
if not isinstance(identifier, str): if not isinstance(suffix, str):
identifier = str(identifier) suffix = str(suffix)
if "." in identifier: if '.' in suffix:
identifier = identifier.replace(".", "_") suffix = suffix.replace('.', '_')
return identifier return suffix
def name_could_conflict(self, dynchild, child): def get_suffixes(self,
return child.impl_getname().startswith(dynchild.impl_getname()) config_bag: ConfigBag,
*,
dynoption=None,
) -> List[str]:
"""get dynamic suffixes
"""
if dynoption:
self_opt = dynoption
else:
self_opt = self
option_bag = OptionBag(self_opt,
None,
config_bag,
properties=None,
)
values = self._suffixes.execute(option_bag)
if values is None:
values = []
values_ = []
if __debug__:
if not isinstance(values, list):
raise ValueError(_('DynOptionDescription suffixes for '
f'option "{self.impl_get_display_name()}", is not '
f'a list ({values})'))
for val in values:
cval = self.convert_suffix_to_path(val)
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
if __debug__ and cval is not None:
raise ValueError(_('invalid suffix "{}" for option "{}"'
'').format(cval,
self.impl_get_display_name()))
else:
values_.append(val)
if __debug__ and len(values_) > len(set(values_)):
raise ValueError(_(f'DynOptionDescription "{self._name}" suffixes return a list with '
f'same values "{values_}"'''))
return values_
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
return True return True
def option_is_self( def option_is_self(self,
self, option,
option, ) -> bool:
) -> bool: return option == self or \
return option == self (option.impl_is_sub_dyn_optiondescription() and option.opt == self)
def impl_getname(self, identifier=None) -> str: def split_path(self,
"""get name""" option,
name = super().impl_getname() *,
if identifier is None: dynoption=None,
return name ) -> Tuple[str, str]:
path_identifier = self.convert_identifier_to_path(identifier) """self.impl_getpath() is something like root.xxx.dynoption_path
return name + path_identifier option.impl_getpath() is something like root.xxx.dynoption_path.sub.path
must return ('root.xxx.', '.sub')
"""
if dynoption is None:
self_path = self.impl_getpath()
else:
self_path = dynoption.impl_getpath()
root_path = self_path.rsplit('.', 1)[0] if '.' in self_path else None
#
if self.option_is_self(option):
sub_path = ''
else:
option_path = option.impl_getpath()
if root_path:
if isinstance(option, SynDynLeadership):
count_root_path = option_path.count('.') - root_path.count('.')
root_path = option_path.rsplit('.', count_root_path)[0]
root_path += '.'
self_number_child = self_path.count('.') + 1
option_sub_path = option_path.split('.', self_number_child)[-1]
sub_path = '.' + option_sub_path.rsplit('.', 1)[0] if '.' in option_sub_path else ''
return root_path, sub_path
def get_identifiers( def get_sub_children(self,
self, option,
parent: "SubConfig", config_bag,
*, *,
uncalculated: bool = False, index=None,
from_display_name: bool = False, properties=undefined,
convert: bool = False, dynoption=None,
) -> List[str]: ):
"""get dynamic identifiers""" root_path, sub_path = self.split_path(option,
subconfig = parent.get_child( dynoption=dynoption,
self, )
None, for suffix in self.get_suffixes(config_bag,
False, dynoption=dynoption,
properties=None, ):
check_dynamic_without_identifiers=False, if self.option_is_self(option):
) parent_path = root_path
identifiers = self._identifiers elif root_path:
if isinstance(identifiers, list): parent_path = root_path + self.impl_getname(suffix) + sub_path
identifiers = identifiers.copy()
if uncalculated:
return identifiers
values = get_calculated_value(
subconfig,
identifiers,
validate_properties=False,
)[0]
if values is None:
values = []
if __debug__:
if not isinstance(values, list):
if not from_display_name:
name = self.impl_get_display_name(subconfig, with_quote=True)
else:
name = self.impl_getname()
raise ValueError(
_(
"DynOptionDescription identifiers for option {0}, is not a list ({1})"
).format(name, values)
)
values_ = []
for val in values:
cval = self.convert_identifier_to_path(val)
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:
if __debug__ and cval is not None:
raise ValueError(
_('invalid identifier "{}" for option {}' "").format(
cval, self.impl_get_display_name(subconfig, with_quote=True)
)
)
elif convert:
values_.append(cval)
else: else:
values_.append(val) parent_path = self.impl_getname(suffix) + sub_path
if ( yield OptionBag(option.to_dynoption(parent_path,
__debug__ [suffix],
and "demoting_error_warning" not in subconfig.config_bag.properties ),
and len(values_) > len(set(values_)) index,
): config_bag,
raise ValueError( properties=properties,
_( ori_option=option
'DynOptionDescription "{0}" identifiers return a list with same values "{1}"' )
).format(self._name, values_)
) def impl_getname(self, suffix=None) -> str:
return values_ """get name
"""
name = super().impl_getname()
if suffix is None:
return name
path_suffix = self.convert_suffix_to_path(suffix)
return name + path_suffix

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""EmailOption""" """EmailOption
"""
import re import re
from ..i18n import _ from ..i18n import _
@ -26,11 +27,8 @@ from .stroption import RegexpOption
class EmailOption(RegexpOption): class EmailOption(RegexpOption):
"""represents a choice of an email""" """represents a choice of an email
"""
__slots__ = tuple() __slots__ = tuple()
_regexp = re.compile( _regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" _type = _('email address')
)
_type = "email address"
_t_type = _("email address")

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,68 +18,21 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""FilenameOption""" """FilenameOption
from pathlib import Path """
from ..i18n import _ from ..i18n import _
from ..error import display_list
from .stroption import StrOption from .stroption import StrOption
class FilenameOption(StrOption): class FilenameOption(StrOption):
"""validate file or directory name""" """represents a choice of a file name
"""
__slots__ = tuple() __slots__ = tuple()
_type = "file name" _type = _('file name')
_t_type = _("file name")
def __init__( def validate(self,
self, value: str,
name: str, ) -> None:
*args,
allow_relative=False,
test_existence=False,
types=["file", "directory"],
**kwargs,
):
if not isinstance(types, list):
raise ValueError(
_('types parameter must be a list, not "{0}" for "{1}"').format(
types, name
)
)
for typ in types:
if typ not in ["file", "directory"]:
raise ValueError(f'unknown type "{typ}" for "{name}"')
extra = {
"allow_relative": allow_relative,
"test_existence": test_existence,
"types": types,
}
super().__init__(name, *args, extra=extra, **kwargs)
def validate(
self,
value: str,
) -> None:
super().validate(value) super().validate(value)
if not self.impl_get_extra("allow_relative") and not value.startswith("/"): if not value.startswith('/'):
raise ValueError(_('must starts with "/"')) raise ValueError(_('must starts with "/"'))
if value is not None and self.impl_get_extra("test_existence"):
types = self.impl_get_extra("types")
file = Path(value)
found = False
if "file" in types and file.is_file():
found = True
if not found and "directory" in types and file.is_dir():
found = True
if not found:
translated_types = [
{"file": _("file"), "directory": _("directory")}.get(typ)
for typ in types
]
raise ValueError(
_("cannot find this {0}").format(
display_list(translated_types, separator="or"), value
)
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,19 +18,20 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""FloatOption""" """FloatOption
"""
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
class FloatOption(Option): class FloatOption(Option):
"""represents a choice of a floating point number""" """represents a choice of a floating point number
"""
__slots__ = tuple() __slots__ = tuple()
_type = "float" _type = _('float')
_t_type = _("float")
def validate(self, value: float) -> None: def validate(self,
value: float) -> None:
if not isinstance(value, float): if not isinstance(value, float):
raise ValueError() raise ValueError()

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""IntOption""" """IntOption
"""
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
@ -26,50 +27,41 @@ from .option import Option
class IntOption(Option): class IntOption(Option):
"represents a choice of an integer" "represents a choice of an integer"
__slots__ = tuple() __slots__ = tuple()
_type = "integer" _type = _('integer')
_t_type = _("integer")
def __init__( def __init__(self,
self, *args,
*args, min_number=None,
min_number=None, max_number=None,
max_number=None, **kwargs):
min_integer=None,
max_integer=None,
**kwargs,
):
extra = {} extra = {}
if min_number is not None: if min_number is not None:
extra["min_integer"] = min_number extra['min_number'] = min_number
if min_integer is not None:
extra["min_integer"] = min_integer
if max_number is not None: if max_number is not None:
extra["max_integer"] = max_number extra['max_number'] = max_number
if max_integer is not None:
extra["max_integer"] = max_integer
super().__init__(*args, extra=extra, **kwargs) super().__init__(*args, extra=extra, **kwargs)
def validate( def validate(self,
self, value: int,
value: int, ) -> None:
) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_("it's not an integer")) raise ValueError()
def second_level_validation(self, value, warnings_only): def second_level_validation(self,
min_integer = self.impl_get_extra("min_integer") value,
if min_integer is not None and value < min_integer: warnings_only):
min_number = self.impl_get_extra('min_number')
if min_number is not None and value < min_number:
if warnings_only: if warnings_only:
msg = _('value should be equal or greater than "{0}"') msg = 'value should be greater than "{0}"'
else: else:
msg = _('value must be equal or greater than "{0}"') msg = 'value must be greater than "{0}"'
raise ValueError(msg.format(min_integer)) raise ValueError(_(msg).format(min_number))
max_integer = self.impl_get_extra("max_integer") max_number = self.impl_get_extra('max_number')
if max_integer is not None and value > max_integer: if max_number is not None and value > max_number:
if warnings_only: if warnings_only:
msg = _('value should be less than "{0}"') msg = 'value should be less than "{0}"'
else: else:
msg = _('value must be less than "{0}"') msg = 'value must be less than "{0}"'
raise ValueError(msg.format(max_integer)) raise ValueError(_(msg).format(max_number))

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""IPOption""" """IPOption
"""
from ipaddress import ip_address, ip_interface from ipaddress import ip_address, ip_interface
from ..i18n import _ from ..i18n import _
@ -26,36 +27,32 @@ from .stroption import StrOption
class IPOption(StrOption): class IPOption(StrOption):
"""represents the choice of an ip""" """represents the choice of an ip
"""
__slots__ = tuple() __slots__ = tuple()
_type = "IP" _type = _('IP')
_t_type = _("IP")
def __init__( def __init__(self,
self, *args,
*args, private_only=False,
private_only=False, allow_reserved=False,
allow_reserved=False, cidr=False,
cidr=False, extra=None,
extra=None, **kwargs):
**kwargs,
):
if extra is None: if extra is None:
extra = {} extra = {}
extra["private_only"] = private_only extra['_private_only'] = private_only
extra["allow_reserved"] = allow_reserved extra['_allow_reserved'] = allow_reserved
extra["cidr"] = cidr extra['_cidr'] = cidr
super().__init__(*args, extra=extra, **kwargs) super().__init__(*args,
extra=extra,
**kwargs)
def _validate_cidr(self, value): def _validate_cidr(self, value):
try: try:
ip_obj = ip_interface(value) ip_obj = ip_interface(value)
except ValueError as err: except ValueError as err:
raise ValueError() from err raise ValueError() from err
self._second_level_cidr(ip_obj)
def _second_level_cidr(self, ip_obj):
if ip_obj.ip == ip_obj.network.network_address: if ip_obj.ip == ip_obj.network.network_address:
raise ValueError(_("it's in fact a network address")) raise ValueError(_("it's in fact a network address"))
if ip_obj.ip == ip_obj.network.broadcast_address: if ip_obj.ip == ip_obj.network.broadcast_address:
@ -67,24 +64,27 @@ class IPOption(StrOption):
except ValueError as err: except ValueError as err:
raise ValueError() from err raise ValueError() from err
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
if self.impl_get_extra("cidr"): if self.impl_get_extra('_cidr'):
if "/" not in value: if '/' not in value:
raise ValueError(_('CIDR address must have a "/"')) raise ValueError(_('CIDR address must have a "/"'))
self._validate_cidr(value) self._validate_cidr(value)
else: else:
self._validate_ip(value) self._validate_ip(value)
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
ip_obj = ip_interface(value) ip_obj = ip_interface(value)
if not self.impl_get_extra("allow_reserved") and ip_obj.is_reserved: if not self.impl_get_extra('_allow_reserved') and ip_obj.is_reserved:
if warnings_only: if warnings_only:
msg = _("shouldn't be reserved IP") msg = _("shouldn't be reserved IP")
else: else:
msg = _("mustn't be reserved IP") msg = _("mustn't be reserved IP")
raise ValueError(msg) raise ValueError(msg)
if self.impl_get_extra("private_only") and not ip_obj.is_private: if self.impl_get_extra('_private_only') and not ip_obj.is_private:
if warnings_only: if warnings_only:
msg = _("should be private IP") msg = _("should be private IP")
else: else:

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"Leadership support" "Leadership support"
# Copyright (C) 2014-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2014-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -24,10 +24,9 @@ from typing import List, Iterator, Optional
from ..i18n import _ from ..i18n import _
from ..setting import groups, undefined, ALLOWED_LEADER_PROPERTIES from ..setting import groups, undefined, OptionBag, ALLOWED_LEADER_PROPERTIES
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynLeadership
# from .syndynoption import SynDynLeadership
from .baseoption import BaseOption from .baseoption import BaseOption
from .option import Option from .option import Option
from ..error import LeadershipError from ..error import LeadershipError
@ -35,94 +34,58 @@ from ..autolib import Calculation
class Leadership(OptionDescription): class Leadership(OptionDescription):
"""Leadership""" """Leadership
"""
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
__slots__ = ( __slots__ = ('leader',
"leader", 'followers',
"followers", )
)
def __init__( def __init__(self,
self, name: str,
name: str, doc: str,
doc, children: List[BaseOption],
children: List[BaseOption], properties=None) -> None:
**kwargs, super().__init__(name,
) -> None: doc,
if "group_type" in kwargs: children,
raise LeadershipError(name, "leadership-group_type") properties=properties)
super().__init__(
name,
doc,
children,
**kwargs,
)
self._group_type = groups.leadership self._group_type = groups.leadership
followers = [] followers = []
if len(children) < 2: if len(children) < 2:
raise ValueError( raise ValueError(_('a leader and a follower are mandatories in leadership "{}"'
_( '').format(name))
'a leader and a follower are mandatories in leadership "{}"' "" leader = children[0]
).format(name)
)
for idx, child in enumerate(children): for idx, child in enumerate(children):
if __debug__: if __debug__:
self._check_child_is_valid(child, idx, children) self._check_child_is_valid(child)
if idx != 0: if idx != 0:
if __debug__: if __debug__:
self._check_default_value(child) self._check_default_value(child)
# remove empty property for follower # remove empty property for follower
if not child.impl_is_submulti(): child._properties = frozenset(child._properties - {'empty', 'unique'})
child._properties = frozenset(
child._properties - {"empty", "unique"}
)
followers.append(child) followers.append(child)
child._add_dependency(self, "leadership") child._add_dependency(self)
child._leadership = weakref.ref(self) child._leadership = weakref.ref(self)
if __debug__: if __debug__:
leader = children[0]
for prop in leader.impl_getproperties(): for prop in leader.impl_getproperties():
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance( if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(prop, Calculation):
prop, Calculation raise LeadershipError(_('leader cannot have "{}" property').format(prop))
):
raise LeadershipError(name, "leadership-wrong_property", prop=prop)
def _check_child_is_valid( def _check_child_is_valid(self, child: BaseOption):
self,
child: BaseOption,
index: int,
children: [BaseOption],
) -> None:
if child.impl_is_symlinkoption(): if child.impl_is_symlinkoption():
if not index: raise ValueError(_('leadership "{0}" shall not have '
raise ValueError( "a symlinkoption").format(self.impl_get_display_name()))
_("leadership {0} shall not have " "a symlinkoption").format(
self.impl_get_display_name(None, with_quote=True)
)
)
return
if not isinstance(child, Option): if not isinstance(child, Option):
raise ValueError( raise ValueError(_('leadership "{0}" shall not have '
_("leadership {0} shall not have " "a subgroup").format( 'a subgroup').format(self.impl_get_display_name()))
self.impl_get_display_name(None, with_quote=True)
)
)
if not child.impl_is_multi(): if not child.impl_is_multi():
raise ValueError( raise ValueError(_('only multi option allowed in leadership "{0}" but option '
_( '"{1}" is not a multi'
"only multi option are allowed in leadership {0} but option " '').format(self.impl_get_display_name(),
"{1} is not a multi" child.impl_get_display_name()))
""
).format(
self.impl_get_display_name(None, with_quote=True),
child.impl_get_display_name(None, with_quote=True),
)
)
def _check_default_value(self, child: BaseOption): def _check_default_value(self, child: BaseOption):
if child.impl_is_symlinkoption():
return
default = child.impl_getdefault() default = child.impl_getdefault()
if default != []: if default != []:
if child.impl_is_submulti() and isinstance(default, (list, tuple)): if child.impl_is_submulti() and isinstance(default, (list, tuple)):
@ -136,174 +99,158 @@ class Leadership(OptionDescription):
else: else:
calculation = isinstance(default, Calculation) calculation = isinstance(default, Calculation)
if not calculation: if not calculation:
raise ValueError( raise ValueError(_('not allowed default value for follower option '
_( f'"{child.impl_get_display_name()}" in leadership '
"not allowed default value for follower option {0} in leadership {1}" f'"{self.impl_get_display_name()}"'))
).format(
child.impl_get_display_name(None, with_quote=True),
self.impl_get_display_name(None, with_quote=True),
)
)
def _setsubdyn( def _setsubdyn(self,
self, subdyn,
subdyn, ) -> None:
) -> None:
for chld in self._children[1]: for chld in self._children[1]:
chld._setsubdyn(subdyn) chld._setsubdyn(subdyn)
super()._setsubdyn(subdyn) super()._setsubdyn(subdyn)
def is_leader( def is_leader(self,
self, opt: Option,
opt: Option, ) -> bool:
) -> bool: """the option is the leader
"""the option is the leader""" """
leader = self.get_leader() leader = self.get_leader()
if opt.impl_is_dynsymlinkoption(): return opt == leader or (opt.impl_is_dynsymlinkoption() and opt.opt == leader)
opt = opt.opt
return opt == leader
def get_leader(self) -> Option: def get_leader(self) -> Option:
"""get leader""" """get leader
"""
return self._children[1][0] return self._children[1][0]
def get_followers(self) -> Iterator[Option]: def get_followers(self) -> Iterator[Option]:
"""get all followers""" """get all followers
"""
for follower in self._children[1][1:]: for follower in self._children[1][1:]:
yield follower yield follower
def in_same_leadership( def in_same_leadership(self,
self, opt: Option,
opt: Option, ) -> bool:
) -> bool: """check if followers are in same leadership
"""check if followers are in same leadership""" """
if opt.impl_is_dynsymlinkoption(): if opt.impl_is_dynsymlinkoption():
opt = opt.opt opt = opt.opt
return opt in self._children[1] return opt in self._children[1]
def reset(self, parent: "SubConfig") -> None: def reset(self, config_bag: 'ConfigBag') -> None:
"""reset follower value""" """reset follower value
values = parent.config_bag.context.get_values() """
for follower in self.get_followers():
subconfig_follower = parent.get_child(
follower,
None,
False,
)
values.reset(
subconfig_follower,
validate=False,
)
def follower_force_store_value(
self,
value,
subconfig: "SubConfig",
owner,
) -> None:
"""apply force_store_value to follower"""
if not value:
return
config_bag = subconfig.config_bag
values = config_bag.context.get_values() values = config_bag.context.get_values()
for idx, follower in enumerate(self.get_children()): config_bag = config_bag.copy()
sub_subconfig = subconfig.get_child( config_bag.remove_validation()
follower, for follower in self.get_followers():
None, soption_bag = OptionBag(follower,
False, None,
config_bag=config_bag, config_bag,
) )
if "force_store_value" not in sub_subconfig.properties: values.reset(soption_bag)
continue
self_path = sub_subconfig.path
if not idx:
# it's a master
apply_requires = True
indexes = [None]
else:
apply_requires = False
indexes = range(len(value))
for index in indexes:
i_sub_subconfig = subconfig.get_child(
follower,
index,
False,
config_bag=config_bag,
)
values.set_storage_value(
self_path,
index,
values.get_value(i_sub_subconfig)[0],
owner,
)
def pop( def follower_force_store_value(self,
self, value,
subconfig: "SubConfig", config_bag: 'ConfigBag',
index: int, owner,
*, dyn=None,
followers: Optional[List[Option]] = undefined, ) -> None:
) -> None: """apply force_store_value to follower
"""pop leader value and follower's one""" """
if value:
if dyn is None:
dyn = self
values = config_bag.context.get_values()
for idx, follower in enumerate(dyn.get_children(config_bag)):
if not idx:
# it's a master
apply_requires = True
indexes = [None]
else:
apply_requires = False
indexes = range(len(value))
foption_bag = OptionBag(follower,
None,
config_bag,
apply_requires=apply_requires,
)
if 'force_store_value' not in foption_bag.properties:
continue
for index in indexes:
foption_bag_index = OptionBag(follower,
index,
config_bag,
)
values.set_storage_value(foption_bag_index.path,
index,
values.get_value(foption_bag_index)[0],
owner,
)
def pop(self,
index: int,
config_bag: 'ConfigBag',
followers: Optional[List[Option]]=undefined,
) -> None:
"""pop leader value and follower's one
"""
if followers is undefined: if followers is undefined:
# followers are not undefined only in SynDynLeadership # followers are not undefined only in SynDynLeadership
followers = self.get_followers() followers = self.get_followers()
config_bag = subconfig.config_bag.copy() config_bag = config_bag.copy()
config_bag.remove_validation() config_bag.remove_validation()
values = config_bag.context.get_values() values = config_bag.context.get_values()
for follower in followers: for follower in followers:
sub_subconfig = subconfig.parent.get_child( soption_bag = OptionBag(follower,
follower, index,
index, config_bag,
True, properties=set(), # do not check force_default_on_freeze
properties=set(), # do not check force_default_on_freeze # or force_metaconfig_on_freeze
# or force_metaconfig_on_freeze )
config_bag=config_bag, values.reduce_index(soption_bag)
)
values.reduce_index(sub_subconfig)
def reset_cache( def reset_cache(self,
self, path: str,
path: str, config_bag: 'ConfigBag',
config_bag: "ConfigBag", resetted_opts: List[Option],
resetted_opts: List[Option], ) -> None:
) -> None: self._reset_cache(path,
self._reset_cache( self.get_leader(),
path, self.get_followers(),
self.get_leader(), config_bag,
self.get_followers(), resetted_opts,
config_bag, )
resetted_opts,
)
def _reset_cache( def _reset_cache(self,
self, path: str,
path: str, leader: Option,
leader: Option, followers: List[Option],
followers: List[Option], config_bag: 'ConfigBag',
config_bag: "ConfigBag", resetted_opts: List[Option],
resetted_opts: List[Option], ) -> None:
) -> None: super().reset_cache(path,
super().reset_cache( config_bag,
path, resetted_opts,
config_bag, )
resetted_opts, leader.reset_cache(leader.impl_getpath(),
) config_bag,
leader_path = leader.impl_getpath() None)
if leader_path not in resetted_opts:
leader.reset_cache(
leader_path,
config_bag,
resetted_opts,
)
for follower in followers: for follower in followers:
follower_path = follower.impl_getpath() follower.reset_cache(follower.impl_getpath(),
if follower_path not in resetted_opts: config_bag,
follower.reset_cache( None,
follower_path, )
config_bag,
resetted_opts,
)
def impl_is_leadership(self) -> None: def impl_is_leadership(self) -> None:
return True return True
def to_dynoption(self,
rootpath: str,
suffixes: Optional[list],
) -> SynDynLeadership:
return SynDynLeadership(self,
rootpath,
suffixes,
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2020-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2020-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""MACOption""" """MACOption
"""
import re import re
from ..i18n import _ from ..i18n import _
@ -26,9 +27,8 @@ from .stroption import RegexpOption
class MACOption(RegexpOption): class MACOption(RegexpOption):
"""represents the choice of a mac address""" """represents the choice of a mac address
"""
__slots__ = tuple() __slots__ = tuple()
_regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$") _regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$")
_type = "mac address" _type = _('mac address')
_t_type = _("mac address")

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,25 +18,26 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""NetmaskOption""" """NetmaskOption
"""
from ipaddress import ip_network from ipaddress import ip_network
from ..i18n import _ from ..i18n import _
from .stroption import StrOption from .stroption import StrOption
class NetmaskOption(StrOption): class NetmaskOption(StrOption):
"""represents the choice of a netmask""" """represents the choice of a netmask
"""
__slots__ = tuple() __slots__ = tuple()
_type = "netmask address" _type = _('netmask address')
_t_type = _("netmask address")
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
for val in value.split("."): for val in value.split('.'):
if val.startswith("0") and len(val) > 1: if val.startswith("0") and len(val) > 1:
raise ValueError() raise ValueError()
try: try:
ip_network(f"0.0.0.0/{value}") ip_network(f'0.0.0.0/{value}')
except ValueError as err: except ValueError as err:
raise ValueError() from err raise ValueError() from err

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,29 +18,51 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""NetworkOption""" """NetworkOption
"""
from ipaddress import ip_network from ipaddress import ip_network
from ..i18n import _ from ..i18n import _
from .ipoption import IPOption
from .stroption import StrOption from .stroption import StrOption
class NetworkOption(IPOption): class NetworkOption(StrOption):
"represents the choice of a network" "represents the choice of a network"
__slots__ = tuple() __slots__ = tuple()
_type = "network address" _type = _('network address')
_t_type = _("network address")
def __init__(self, *args, cidr=False, **kwargs): def __init__(self,
super().__init__(*args, cidr=cidr, **kwargs) *args,
cidr=False,
**kwargs):
extra = {'_cidr': cidr}
super().__init__(*args,
extra=extra,
**kwargs)
def _second_level_cidr(self, ip_obj): def validate(self,
if ip_obj.ip != ip_obj.network.network_address: value: str) -> None:
raise ValueError(_("it's not a network address")) super().validate(value)
if value.count('.') != 3:
raise ValueError()
cidr = self.impl_get_extra('_cidr')
if cidr:
if '/' not in value:
raise ValueError(_('must use CIDR notation'))
value_ = value.split('/')[0]
else:
value_ = value
for val in value_.split('.'):
if val.startswith("0") and len(val) > 1:
raise ValueError()
try:
ip_network(value)
except ValueError as err:
raise ValueError() from err
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
if ip_network(value).network_address.is_reserved: if ip_network(value).network_address.is_reserved:
if warnings_only: if warnings_only:
msg = _("shouldn't be reserved network") msg = _("shouldn't be reserved network")

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"option types and option description" "option types and option description"
# Copyright (C) 2012-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -25,9 +25,10 @@ from itertools import chain
from .baseoption import BaseOption, submulti from .baseoption import BaseOption, submulti
from ..i18n import _ from ..i18n import _
from ..setting import undefined from ..setting import undefined, OptionBag
from ..autolib import Calculation from ..autolib import Calculation, ParamOption, ParamInformation, ParamSelfInformation
from ..error import ValueWarning, ValueErrorWarning, ValueOptionError from ..error import ValueWarning, ValueErrorWarning, ValueOptionError
from .syndynoption import SynDynOption
class Option(BaseOption): class Option(BaseOption):
@ -37,45 +38,36 @@ 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__ = ( __slots__ = ('_extra',
"_extra", '_warnings_only',
"_warnings_only", # multi
# multi '_multi',
"_multi", # value
# value '_default',
"_default", '_default_multi',
"_default_multi", #
# '_validators',
"_validators", #
# '_dependencies_information',
"_leadership", '_leadership',
"_choice_values", '_choice_values',
"_choice_values_params", '_choice_values_params',
) )
_type = None _type = None
_t_type = None def __init__(self,
_do_not_display_value_in_error = False name: str,
doc: str,
def __init__( default: Any=undefined,
self, default_multi: Any=None,
name: str, multi: bool=False,
doc: str, validators: Optional[List[Calculation]]=None,
default: Any = undefined, properties: Optional[List[str]]=None,
default_multi: Any = None, warnings_only: bool=False,
multi: bool = False, extra: Optional[Dict]=None):
validators: Optional[List[Calculation]] = None,
properties: Optional[List[str]] = None,
warnings_only: bool = False,
extra: Optional[Dict] = None,
informations: Optional[Dict] = None,
):
_setattr = object.__setattr__ _setattr = object.__setattr__
if not multi and default_multi is not None: if not multi and default_multi is not None:
raise ValueError( raise ValueError(_("default_multi is set whereas multi is False"
_( " in option: {0}").format(name))
"default_multi is set whereas multi is False" " in option: {0}"
).format(name)
)
if default is undefined: if default is undefined:
if multi is False: if multi is False:
default = None default = None
@ -91,149 +83,155 @@ class Option(BaseOption):
is_multi = True is_multi = True
_multi = submulti _multi = submulti
else: else:
raise ValueError( raise ValueError(_('invalid multi type "{}" for "{}"').format(multi,
_('invalid multi type "{}" for "{}"').format( name,
multi, ))
name,
)
)
if _multi != 1: if _multi != 1:
_setattr(self, "_multi", _multi) _setattr(self, '_multi', _multi)
if multi is not False and default is None: if multi is not False and default is None:
default = [] default = []
super().__init__( super().__init__(name,
name, doc,
doc, properties=properties,
informations, is_multi=is_multi)
properties=properties,
is_multi=is_multi,
)
if validators is not None: if validators is not None:
if __debug__ and not isinstance(validators, list): if __debug__ and not isinstance(validators, list):
raise ValueError( raise ValueError(_(f'validators must be a list of Calculation for "{name}"'))
_('validators must be a list of Calculation for "{0}"').format(name)
)
for validator in validators: for validator in validators:
if __debug__ and not isinstance(validator, Calculation): if __debug__ and not isinstance(validator, Calculation):
raise ValueError( raise ValueError(_('validators must be a Calculation for "{}"').format(name))
_('validators must be a Calculation for "{0}"').format(name)
)
self.value_dependency(validator) self.value_dependency(validator)
self._validators = tuple(validators) self._validators = tuple(validators)
if extra is not None and extra != {}: if extra is not None and extra != {}:
_setattr(self, "_extra", extra) _setattr(self, '_extra', extra)
if warnings_only is True: if warnings_only is True:
_setattr(self, "_warnings_only", warnings_only) _setattr(self, '_warnings_only', warnings_only)
if is_multi and default_multi is not None: if is_multi and default_multi is not None:
def test_multi_value(value): def test_multi_value(value):
if isinstance(value, Calculation): if isinstance(value, Calculation):
return return
option_bag = OptionBag(self,
None,
undefined,
properties=None,
)
try: try:
self.validate(value) self.validate(value)
self.validate_with_option( self.validate_with_option(value,
value, option_bag,
None, loaded=True,
loaded=True, )
)
except ValueError as err: except ValueError as err:
str_err = str(err) str_err = str(err)
if not str_err: if not str_err:
raise ValueError( raise ValueError(_('invalid default_multi value "{0}" '
_( 'for option "{1}"').format(str(value),
'invalid default_multi value "{0}" ' "for option {1}" self.impl_get_display_name())
).format( ) from err
str(value), raise ValueError(_(f'invalid default_multi value "{value}" for option '
self.impl_get_display_name(None, with_quote=True), f'"{self.impl_get_display_name()}", {str_err}')
) ) from err
) from err
raise ValueError(
_(
'invalid default_multi value "{0}" for option {1}, {2}'
).format(
value,
self.impl_get_display_name(None, with_quote=True),
str_err,
)
) from err
if _multi is submulti: if _multi is submulti:
if not isinstance(default_multi, Calculation): if not isinstance(default_multi, Calculation):
if not isinstance(default_multi, list): if not isinstance(default_multi, list):
raise ValueError( raise ValueError(_('invalid default_multi value "{0}" '
_( 'for option "{1}", must be a list for a submulti'
'invalid default_multi value "{0}" ' '').format(str(default_multi),
"for option {1}, must be a list for a submulti" self.impl_get_display_name()))
""
).format(
str(default_multi),
self.impl_get_display_name(None, with_quote=True),
)
)
for value in default_multi: for value in default_multi:
test_multi_value(value) test_multi_value(value)
else: else:
test_multi_value(default_multi) test_multi_value(default_multi)
_setattr(self, "_default_multi", default_multi) _setattr(self, '_default_multi', default_multi)
# option_bag = OptionBag(self, option_bag = OptionBag(self,
# None, None,
# undefined, undefined,
# properties=None, properties=None,
# ) )
self_properties = getattr(self, "_properties", {}) self.impl_validate(default,
self.impl_validate( option_bag,
None, loaded=True,
default, )
loaded=True, self.impl_validate(default,
self_properties=self_properties, option_bag,
) check_error=False,
self.impl_validate( loaded=True,
None, )
default,
check_error=False,
loaded=True,
self_properties=self_properties,
)
self.value_dependencies(default) self.value_dependencies(default)
if (is_multi and default != []) or (not is_multi and default is not None): if (is_multi and default != []) or \
(not is_multi and default is not None):
if is_multi and isinstance(default, list): if is_multi and isinstance(default, list):
default = tuple(default) default = tuple(default)
_setattr(self, "_default", default) _setattr(self, '_default', default)
# __________________________________________________________________________ def value_dependencies(self,
value: Any,
) -> Any:
"""parse dependancies to add dependencies
"""
if isinstance(value, list):
for val in value:
if isinstance(value, list):
self.value_dependencies(val)
elif isinstance(value, Calculation):
self.value_dependency(val)
elif isinstance(value, Calculation):
self.value_dependency(value)
def value_dependency(self,
value: Any,
) -> Any:
"""parse dependancy to add dependencies
"""
for param in chain(value.params.args, value.params.kwargs.values()):
if isinstance(param, ParamOption):
# pylint: disable=protected-access
param.option._add_dependency(self)
self._has_dependency = True
elif isinstance(param, ParamInformation):
dest = self
if isinstance(param, ParamSelfInformation):
opt = self
elif param.option:
dest = param.option
opt = self
else:
opt = None
if not getattr(dest, '_dependencies_information', {}):
dest._dependencies_information = {}
dest._dependencies_information.setdefault(param.information_name, []).append(opt)
#__________________________________________________________________________
# option's information # option's information
def impl_is_multi(self) -> bool: def impl_is_multi(self) -> bool:
"""is it a multi option""" """is it a multi option
return getattr(self, "_multi", 1) != 1 """
return getattr(self, '_multi', 1) != 1
def impl_is_submulti(self) -> bool: def impl_is_submulti(self) -> bool:
"""is it a submulti option""" """is it a submulti option
return getattr(self, "_multi", 1) == 2 """
return getattr(self, '_multi', 1) == 2
def impl_is_dynsymlinkoption(self) -> bool: def impl_is_dynsymlinkoption(self) -> bool:
"""is a dynsymlinkoption?""" """is a dynsymlinkoption?
"""
return False return False
def get_type(self, translation=True) -> str: def get_type(self) -> str:
"""get the type of option""" """get the type of option
if translation: """
return self._t_type
return self._type return self._type
def impl_getdefault(self) -> Any: def impl_getdefault(self) -> Any:
"""accessing the default value""" """accessing the default value
"""
is_multi = self.impl_is_multi() is_multi = self.impl_is_multi()
default = getattr(self, "_default", undefined) default = getattr(self, '_default', undefined)
if default is undefined: if default is undefined:
if is_multi: if is_multi:
if self.impl_is_follower(): default = []
if submulti:
default = getattr(self, "_default_multi", [])
else:
default = getattr(self, "_default_multi", None)
else:
default = []
else: else:
default = None default = None
elif is_multi and isinstance(default, tuple): elif is_multi and isinstance(default, tuple):
@ -241,305 +239,240 @@ class Option(BaseOption):
return default return default
def impl_getdefault_multi(self) -> Any: def impl_getdefault_multi(self) -> Any:
"""accessing the default value for a multi""" """accessing the default value for a multi
"""
if self.impl_is_submulti(): if self.impl_is_submulti():
default_value = [] default_value = []
else: else:
default_value = None default_value = None
return getattr(self, "_default_multi", default_value) return getattr(self, '_default_multi', default_value)
def impl_get_extra( def impl_get_extra(self,
self, key: str,
key: str, ) -> Any:
) -> Any: """if extra parameters are store get it
"""if extra parameters are store get it""" """
extra = getattr(self, "_extra", {}) extra = getattr(self, '_extra', {})
if isinstance(extra, tuple): if isinstance(extra, tuple):
if key in extra[0]: if key in extra[0]:
return extra[1][extra[0].index(key)] return extra[1][extra[0].index(key)]
return None return None
return extra.get(key) return extra.get(key)
# __________________________________________________________________________ #__________________________________________________________________________
# validator # validator
def impl_validate( def impl_validate(self,
self, value: Any,
subconfig: Optional["SubConfig"], option_bag: OptionBag,
value: Any, check_error: bool=True,
*, loaded: bool=False,
check_error: bool = True, ) -> bool:
loaded: bool = False,
self_properties: frozenset = frozenset(),
) -> bool:
"""Return True if value is really valid """Return True if value is really valid
If not validate or invalid return it returns False If not validate or invalid return it returns False
""" """
if check_error: config_bag = option_bag.config_bag
if subconfig: force_index = option_bag.index
config_properties = subconfig.config_bag.properties is_warnings_only = getattr(self, '_warnings_only', False)
self_properties = subconfig.properties if check_error and config_bag is not undefined and \
else: not 'validator' in config_bag.properties:
config_properties = {"validator"} return False
if (
"validator" not in config_properties
or "validator" not in self_properties
):
return False
if subconfig:
force_index = subconfig.index
else:
force_index = None
is_warnings_only = getattr(self, "_warnings_only", False)
def _is_not_unique(current_value, values): def _is_not_unique(value, option_bag):
if current_value is None: # if set(value) has not same length than value
if config_bag is undefined or not check_error or \
'unique' not in option_bag.properties:
return return
if not subconfig or not check_error or "unique" not in subconfig.properties: lvalue = [val for val in value if val is not None]
if len(set(lvalue)) == len(lvalue):
return return
indexes = [ for idx, val in enumerate(value):
index for index, value in enumerate(values) if value == current_value if val not in value[idx+1:]:
]
if len(indexes) > 1:
raise ValueError(
_('the value "{}" is not unique' "").format(current_value)
)
def calculation_validator(
val,
_index,
):
for validator in getattr(self, "_validators", []):
calc_is_warnings_only = (
hasattr(validator, "warnings_only") and validator.warnings_only
)
if (check_error and not calc_is_warnings_only) or (
not check_error and calc_is_warnings_only
):
try:
kwargs = {
"allow_value_error": True,
"force_value_warning": calc_is_warnings_only,
}
if _index is not None and subconfig.index == _index:
lsubconfig = subconfig
else:
identifier = subconfig.identifiers
if identifier is not None:
identifier = identifier[-1]
lsubconfig = subconfig.parent.get_child(
subconfig.option,
_index,
False,
properties=subconfig.properties,
identifier=identifier,
name=subconfig.path.rsplit(".", 1)[-1],
check_index=False,
)
kwargs["orig_value"] = value
validator.execute(
lsubconfig,
**kwargs,
)
except ValueWarning as warn:
warnings.warn_explicit(
ValueWarning(
subconfig=subconfig,
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(warn),
index=_index,
),
ValueWarning,
self.__class__.__name__,
319,
)
def do_validation(
_value,
_index,
):
#
if _value is None:
return
if isinstance(_value, list):
raise ValueError(_("it must not be a list"))
if isinstance(_value, Calculation) and not subconfig:
return
# option validation
if check_error:
self.validate(_value)
self.validate_with_option(
_value,
subconfig,
loaded=loaded,
)
# second level validation
if (check_error and not is_warnings_only) or (
not check_error and is_warnings_only
):
try:
self.second_level_validation(_value, is_warnings_only)
except ValueError as err:
if is_warnings_only:
warnings.warn_explicit(
ValueWarning(
subconfig=subconfig,
val=_value,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=_index,
),
ValueWarning,
self.__class__.__name__,
0,
)
else:
raise err from err
# ?
if not loaded:
calculation_validator(
_value,
_index,
)
err_index = force_index
ret = True
if not self.impl_is_multi():
try:
do_validation(
value,
None,
)
except ValueError as err:
self.validate_parse_error(value, err_index, err, subconfig)
ret = False
elif force_index is not None:
if self.impl_is_submulti():
if not isinstance(value, list):
raise ValueError(_("it must be a list"))
for val in value:
try:
do_validation(
val,
force_index,
)
_is_not_unique(val, value)
except ValueError as err:
self.validate_parse_error(val, err_index, err, subconfig)
ret = False
else:
try:
do_validation(
value,
force_index,
)
except ValueError as err:
self.validate_parse_error(value, err_index, err, subconfig)
ret = False
elif isinstance(value, Calculation) and not subconfig:
pass
elif self.impl_is_submulti():
for err_index, lval in enumerate(value):
if isinstance(lval, Calculation):
continue continue
if not isinstance(lval, list): raise ValueError(_('the value "{}" is not unique'
raise ValueError( '').format(val))
_('which "{}" must be a list of list' "").format(lval)
) def calculation_validator(val,
for val in lval: _index,
):
for validator in getattr(self, '_validators', []):
calc_is_warnings_only = hasattr(validator, 'warnings_only') and \
validator.warnings_only
if ((check_error and not calc_is_warnings_only) or
(not check_error and calc_is_warnings_only)):
try: try:
do_validation(val, err_index) kwargs = {'allow_value_error': True,
_is_not_unique(val, lval) 'force_value_warning': calc_is_warnings_only,
}
if _index is not None and option_bag.index == _index:
soption_bag = option_bag
else:
soption_bag = option_bag.copy()
soption_bag.index = _index
kwargs['orig_value'] = value
validator.execute(soption_bag,
**kwargs,
)
except ValueWarning as warn:
warnings.warn_explicit(ValueWarning(val,
self.get_type(),
self,
str(warn),
_index,
),
ValueWarning,
self.__class__.__name__, 319)
def do_validation(_value,
_index,
):
#
if isinstance(_value, list):
raise ValueError(_('which must not be a list').format(_value,
self.impl_get_display_name()),
)
if isinstance(_value, Calculation) and config_bag is undefined:
return
if _value is not None:
if check_error:
# option validation
self.validate(_value)
self.validate_with_option(_value,
option_bag,
loaded=loaded,
)
if ((check_error and not is_warnings_only) or
(not check_error and is_warnings_only)):
try:
self.second_level_validation(_value,
is_warnings_only)
except ValueError as err: except ValueError as err:
self.validate_parse_error(val, err_index, err, subconfig) if is_warnings_only:
ret = False warnings.warn_explicit(ValueWarning(_value,
elif not isinstance(value, list): self.get_type(),
msg = ValueError(_("it must be a list")) self,
self.validate_parse_error(value, None, msg, subconfig) str(err),
ret = False _index),
else: ValueWarning,
# FIXME suboptimal, not several time for whole=True! self.__class__.__name__, 0)
for err_index, val in enumerate(value): else:
try: raise err
do_validation( if not loaded:
val, calculation_validator(_value,
err_index, _index,
) )
_is_not_unique(val, value) val = value
except ValueError as err: err_index = force_index
self.validate_parse_error(val, err_index, err, subconfig) try:
ret = False if not self.impl_is_multi():
# return False do_validation(val, None)
return ret elif force_index is not None:
if self.impl_is_submulti():
if not isinstance(value, list):
raise ValueError(_('which must be a list'))
for val in value:
do_validation(val,
force_index,
)
_is_not_unique(value,
option_bag,
)
else:
do_validation(val,
force_index,
)
elif isinstance(value, Calculation) and config_bag is undefined:
pass
elif self.impl_is_submulti():
for err_index, lval in enumerate(value):
if isinstance(lval, Calculation):
continue
if not isinstance(lval, list):
raise ValueError(_('which "{}" must be a list of list'
'').format(lval))
for val in lval:
do_validation(val,
err_index)
_is_not_unique(lval, option_bag)
elif not isinstance(value, list):
raise ValueError(_('which must be a list'))
else:
# FIXME suboptimal, not several time for whole=True!
for err_index, val in enumerate(value):
do_validation(val,
err_index,
)
_is_not_unique(value, option_bag)
except ValueError as err:
if config_bag is undefined or \
'demoting_error_warning' not in config_bag.properties:
raise ValueOptionError(val,
self.get_type(),
option_bag.ori_option,
str(err),
err_index) from err
warnings.warn_explicit(ValueErrorWarning(val,
self.get_type(),
option_bag.ori_option,
str(err),
err_index),
ValueErrorWarning,
self.__class__.__name__, 0)
return False
return True
def validate_parse_error(self, val, index, err, subconfig): def validate_with_option(self,
if ( value: Any,
not subconfig option_bag: OptionBag,
or "demoting_error_warning" not in subconfig.config_bag.properties loaded: bool,
): ) -> None:
raise ValueOptionError( """validation function with option
subconfig=subconfig, """
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=index,
) from err
warnings.warn_explicit(
ValueErrorWarning(
subconfig=subconfig,
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=index,
),
ValueErrorWarning,
self.__class__.__name__,
0,
)
def validate_with_option( def second_level_validation(self,
self, value: Any,
value: Any, warnings_only: bool,
subconfig: "SubConfig", ) -> None:
*, """less import validation function
loaded: bool, """
) -> None:
"""validation function with option"""
def second_level_validation(
self,
value: Any,
warnings_only: bool,
) -> None:
"""less import validation function"""
def impl_is_leader(self): def impl_is_leader(self):
"""check if option is a leader in a leadership""" """check if option is a leader in a leadership
"""
leadership = self.impl_get_leadership() leadership = self.impl_get_leadership()
if leadership is None: if leadership is None:
return False return False
return leadership.is_leader(self) return leadership.is_leader(self)
def impl_is_follower(self): def impl_is_follower(self):
"""check if option is a leader in a follower""" """check if option is a leader in a follower
"""
leadership = self.impl_get_leadership() leadership = self.impl_get_leadership()
if leadership is None: if leadership is None:
return False return False
return not leadership.is_leader(self) return not leadership.is_leader(self)
def impl_get_leadership(self): def impl_get_leadership(self):
"""get leadership""" """get leadership
leadership = getattr(self, "_leadership", None) """
leadership = getattr(self, '_leadership', None)
if leadership is None: if leadership is None:
return leadership return leadership
# pylint: disable=not-callable #pylint: disable=not-callable
return leadership() return leadership()
def to_dynoption(self,
rootpath: str,
suffixes: list[str],
) -> SynDynOption:
"""tranforme a dynoption to a syndynoption
"""
return SynDynOption(self,
rootpath,
suffixes,
)
def validate(self, value: Any): def validate(self, value: Any):
"""option needs a validate function""" """option needs a validate function
"""
raise NotImplementedError() raise NotImplementedError()

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2014-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2014-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,47 +18,42 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""OptionDescription""" """OptionDescription
"""
import weakref import weakref
from typing import Optional, Iterator, Union, List, Dict from typing import Optional, Iterator, Union, List
from ..i18n import _ from ..i18n import _
from ..setting import ConfigBag, groups, undefined, owners, Undefined from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
from .syndynoptiondescription import SubDynOptionDescription, SynDynOptionDescription
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription from ..error import ConfigError, ConflictError
from ..error import (
ConfigError,
ConflictError,
AttributeOptionError,
PropertiesOptionError,
)
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
"""manage cache for option description""" """manage cache for option description
"""
__slots__ = ( __slots__ = ('_cache_force_store_values',
"_cache_force_store_values", '_cache_dependencies_information',
"_cache_dependencies_information", )
)
def impl_already_build_caches(self) -> bool: def impl_already_build_caches(self) -> bool:
"""is a readonly option?""" """is a readonly option?
"""
return self.impl_is_readonly() return self.impl_is_readonly()
def _build_cache( def _build_cache(self,
self, display_name,
display_name, _consistencies=None,
_consistencies=None, _consistencies_id=0,
_consistencies_id=0, currpath: List[str]=None,
currpath: List[str] = None, cache_option=None,
cache_option=None, force_store_values=None,
force_store_values=None, dependencies_information=None,
dependencies_information=None, ) -> None:
) -> None: """validate options and set option has readonly option
"""validate options and set option has readonly option""" """
# pylint: disable=too-many-branches,too-many-arguments # pylint: disable=too-many-branches,too-many-arguments
# _consistencies is None only when we start to build cache # _consistencies is None only when we start to build cache
if _consistencies is None: if _consistencies is None:
@ -74,344 +69,346 @@ class CacheOptionDescription(BaseOption):
if self.impl_is_readonly(): if self.impl_is_readonly():
# cache already set # cache already set
raise ConfigError( raise ConfigError(_('option description seems to be part of an other '
_("option description seems to be part of an other " "config") 'config'))
) for option in self.get_children(config_bag=undefined, # pylint: disable=no-member
for option in self.get_children(): dyn=False,
):
if __debug__: if __debug__:
cache_option.append(option) cache_option.append(option)
sub_currpath = currpath + [option.impl_getname()] sub_currpath = currpath + [option.impl_getname()]
subpath = ".".join(sub_currpath) subpath = '.'.join(sub_currpath)
if isinstance(option, OptionDescription): if isinstance(option, OptionDescription):
# pylint: disable=protected-access # pylint: disable=protected-access
option._build_cache( option._build_cache(display_name,
display_name, _consistencies,
_consistencies, _consistencies_id,
_consistencies_id, sub_currpath,
sub_currpath, cache_option,
cache_option, force_store_values,
force_store_values, dependencies_information,
dependencies_information, )
) else:
elif not option.impl_is_symlinkoption(): for information, options in option.get_dependencies_information().items():
informations = option.get_dependencies_information()
if informations:
for param in informations.pop(None):
del param.self_option
for (
information,
options,
) in option.get_dependencies_information().items():
if None in options: if None in options:
dependencies_information.setdefault(information, []).append( dependencies_information.setdefault(information, []).append(option)
option if not option.impl_is_symlinkoption():
) properties = option.impl_getproperties()
properties = option.impl_getproperties() if 'force_store_value' in properties:
if "force_store_value" in properties: force_store_values.append(option)
force_store_values.append(option)
if option.impl_is_readonly(): if option.impl_is_readonly():
previous_path = option.impl_getpath() raise ConflictError(_('duplicate option: {0}').format(option))
if "." in previous_path:
previous_path = _('"{0}" option description').format(
previous_path.rsplit(".", 1)[0]
)
else:
previous_path = _("root option description")
if currpath:
current_path = _('"{0}" option description').format(
".".join(currpath)
)
else:
current_path = _("root option description")
raise ConflictError(
_('option "{0}" is include in {1} but is also in {2}').format(
option.impl_get_display_name(None), current_path, previous_path
)
)
if not self.impl_is_readonly() and display_name: if not self.impl_is_readonly() and display_name:
option._display_name_function = ( option._display_name_function = display_name # pylint: disable=protected-access
display_name # pylint: disable=protected-access
)
option._path = subpath # pylint: disable=protected-access option._path = subpath # pylint: disable=protected-access
option._set_readonly() # pylint: disable=protected-access option._set_readonly() # pylint: disable=protected-access
if init: if init:
self._cache_force_store_values = ( self._cache_force_store_values = force_store_values # pylint: disable=attribute-defined-outside-init
force_store_values # pylint: disable=attribute-defined-outside-init
)
self._cache_dependencies_information = dependencies_information # pylint: disable=attribute-defined-outside-init self._cache_dependencies_information = dependencies_information # pylint: disable=attribute-defined-outside-init
self._path = ( self._path = None # pylint: disable=attribute-defined-outside-init,no-member
None # pylint: disable=attribute-defined-outside-init,no-member
)
self._set_readonly() self._set_readonly()
def impl_build_force_store_values( def impl_build_force_store_values(self,
self, config_bag: ConfigBag,
config_bag: ConfigBag, ) -> None:
) -> None: """set value to force_store_values option
"""set value to force_store_values option""" """
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
context = config_bag.context def do_option_bags(option):
if "force_store_value" not in config_bag.properties: if option.issubdyn():
dynopt = option.getsubdyn()
yield from dynopt.get_sub_children(option,
config_bag,
index=None,
)
else:
yield OptionBag(option,
None,
config_bag,
properties=None,
)
if 'force_store_value' not in config_bag.properties:
return return
values = config_bag.context.get_values() values = config_bag.context.get_values()
for option in self._cache_force_store_values: for option in self._cache_force_store_values:
if option.issubdyn():
paths = option.impl_getpath().split(".")
parents = [config_bag.context.get_root(config_bag)]
for name in paths:
new_parents = []
for parent in parents:
doption = parent.option.get_child(
name,
config_bag,
parent,
allow_dynoption=True,
)
try:
if doption.impl_is_dynoptiondescription():
new_parents.extend(
parent.dyn_to_subconfig(
doption,
True,
)
)
else:
new_parents.append(
parent.get_child(
doption,
None,
name=name,
validate_properties=True,
)
)
except PropertiesOptionError:
continue
parents = new_parents
subconfigs = new_parents
else:
try:
subconfigs = [
context.get_sub_config(
config_bag,
option.impl_getpath(),
None,
validate_properties=True,
)
]
except PropertiesOptionError:
continue
if option.impl_is_follower(): if option.impl_is_follower():
for follower_subconfig in subconfigs: leader = option.impl_get_leadership().get_leader()
parent = follower_subconfig.parent for leader_option_bag in do_option_bags(leader):
follower_len = parent.get_length_leadership() leader_option_bag.properties = frozenset()
follower_len = len(values.get_value(leader_option_bag)[0])
if option.issubdyn():
doption = option.to_dynoption(leader_option_bag.option.rootpath,
leader_option_bag.option.get_suffixes(),
)
else:
doption = option
subpath = doption.impl_getpath()
for index in range(follower_len): for index in range(follower_len):
if values.hasvalue( option_bag = OptionBag(doption,
follower_subconfig.path, index,
index=index, config_bag,
): properties=frozenset(),
)
if values.hasvalue(subpath, index=index):
continue continue
idx_follower_subconfig = parent.get_child( value = values.get_value(option_bag)[0]
follower_subconfig.option, if value is None:
index, continue
validate_properties=True, values.set_storage_value(subpath,
) index,
values.set_force_store_value(idx_follower_subconfig) value,
owners.forced,
)
else: else:
for subconfig in subconfigs: for option_bag in do_option_bags(option):
if values.hasvalue(subconfig.path): option_bag.properties = frozenset()
value = values.get_value(option_bag)[0]
if value is None:
continue continue
values.set_force_store_value(subconfig) if values.hasvalue(option_bag.path):
continue
values.set_storage_value(option_bag.path,
None,
value,
owners.forced,
)
class OptionDescriptionWalk(CacheOptionDescription): class OptionDescriptionWalk(CacheOptionDescription):
"""get child of option description""" """get child of option description
"""
__slots__ = ('_children',)
__slots__ = ("_children",) def get_child_not_dynamic(self,
name,
def get_path( allow_dynoption,
self, ):
config_bag,
):
if config_bag is undefined or config_bag.context.get_description() == self:
return ""
return self.impl_getpath()
def get_child_not_dynamic(
self,
name: str,
allow_dynoption: bool,
parent: "SubConfig",
):
if name in self._children[0]: # pylint: disable=no-member if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][ option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member
self._children[0].index(name)
] # pylint: disable=no-member
if option.impl_is_dynoptiondescription() and not allow_dynoption: if option.impl_is_dynoptiondescription() and not allow_dynoption:
if parent.path: raise AttributeError(_(f'unknown option "{name}" '
path = parent.path + "." + name "in root optiondescription (it's a dynamic option)"
else: ))
path = name
raise AttributeOptionError(path, "option-dynamic")
return option return option
def get_child( def get_child(self,
self, name: str,
name: str, config_bag: ConfigBag,
config_bag: ConfigBag, *,
parent: "SubConfig", allow_dynoption: bool=False,
*, ) -> Union[BaseOption, SynDynOptionDescription]:
with_identifier: bool = False, """get a child
allow_dynoption: bool = False, """
) -> Union[BaseOption]:
"""get a child"""
# if not dyn # if not dyn
option = self.get_child_not_dynamic( option = self.get_child_not_dynamic(name,
name, allow_dynoption,
allow_dynoption, )
parent,
)
if option: if option:
return option return option
# if dyn # if dyn
for child in self._children[1]: # pylint: disable=no-member for child in self._children[1]: # pylint: disable=no-member
if not child.impl_is_dynoptiondescription(): if not child.impl_is_dynoptiondescription():
continue continue
for identifier in child.get_identifiers(parent): for suffix in child.get_suffixes(config_bag):
if name != child.impl_getname(identifier): if name != child.impl_getname(suffix):
continue continue
if not with_identifier: return child.to_dynoption(self.impl_getpath(),
return child [suffix],
return identifier, child )
if parent.path is None: if self.impl_get_group_type() == groups.root: # pylint: disable=no-member
path = name raise AttributeError(_(f'unknown option "{name}" '
else: 'in root optiondescription'
path = parent.path + "." + name ))
raise AttributeOptionError(path, "option-not-found") raise AttributeError(_(f'unknown option "{name}" '
f'in optiondescription "{self.impl_get_display_name()}"'
))
def get_children(self) -> List[BaseOption]: def get_children(self,
"""get children""" config_bag: Union[ConfigBag, Undefined],
return self._children[1] *,
dyn: bool=True,
#path: Optional[str]=None,
dynoption=None,
) -> Union[BaseOption, SynDynOptionDescription]:
"""get children
"""
for child in self._children[1]:
if dyn and child.impl_is_dynoptiondescription():
yield from self.get_suffixed_children(dynoption,
[],
config_bag,
child,
)
else:
yield child
def get_path(self,
config_bag,
dynoption,
):
if dynoption:
self_opt = dynoption
else:
self_opt = self
if config_bag is undefined or \
config_bag.context.get_description() == self:
return ''
return self_opt.impl_getpath()
def get_suffixed_children(self,
dynoption,
option_suffixes: list,
config_bag: ConfigBag,
child,
):
root_path = self.get_path(config_bag, dynoption)
for suffix in child.get_suffixes(config_bag,
dynoption=dynoption,
):
yield child.to_dynoption(root_path,
option_suffixes + [suffix],
)
def get_children_recursively(self,
bytype: Optional[BaseOption],
byname: Optional[str],
config_bag: ConfigBag,
self_opt: BaseOption=None,
*,
option_suffixes: Optional[list]=None
) -> Iterator[Union[BaseOption, SynDynOptionDescription]]:
"""get children recursively
"""
if self_opt is None:
self_opt = self
for option in self_opt.get_children(config_bag):
if option.impl_is_optiondescription():
for subopt in option.get_children_recursively(bytype,
byname,
config_bag,
):
yield subopt
elif (byname is None or option.impl_getname() == byname) and \
(bytype is None or isinstance(option, bytype)):
yield option
class OptionDescription(OptionDescriptionWalk): class OptionDescription(OptionDescriptionWalk):
"""Config's schema (organisation, group) and container of Options """Config's schema (organisation, group) and container of Options
The `OptionsDescription` objects lives in the `tiramisu.config.Config`. The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
""" """
__slots__ = ('_group_type',)
__slots__ = ("_group_type",) def __init__(self,
name: str,
def __init__( doc: str,
self, children: List[BaseOption],
name: str, properties=None) -> None:
doc: str,
children: List[BaseOption],
*,
properties=None,
informations: Optional[Dict] = None,
group_type: Optional[groups.GroupType] = groups.default,
) -> None:
""" """
:param children: a list of options (including optiondescriptions) :param children: a list of options (including optiondescriptions)
""" """
assert isinstance(children, list), _( assert isinstance(children, list), _('children in optiondescription "{}" '
'children in optiondescription "{}" ' "must be a list" 'must be a list').format(name)
).format(name) super().__init__(name,
super().__init__( doc=doc,
name, properties=properties)
doc,
informations,
properties=properties,
)
child_names = [] child_names = []
fix_child_names = [] if __debug__:
dynopts = [] dynopt_names = []
for child in children: for child in children:
name = child.impl_getname() name = child.impl_getname()
child_names.append(name) child_names.append(name)
if child.impl_is_dynoptiondescription(): if __debug__ and child.impl_is_dynoptiondescription():
dynopts.append(child) dynopt_names.append(name)
else:
fix_child_names.append(name)
# before sorting # before sorting
children_ = (tuple(child_names), tuple(children)) children_ = (tuple(child_names), tuple(children))
# better performance like this if __debug__:
fix_child_names.sort() # better performance like this
old = None child_names.sort()
for child_name in fix_child_names: old = None
if child_name == old: for child in child_names:
raise ConflictError( if child == old:
_('the option name "{0}" is duplicate in "{1}"').format( raise ConflictError(_('duplicate option name: '
child_name, self.impl_get_display_name(None) '"{0}"').format(child))
) if dynopt_names:
) for dynopt in dynopt_names:
old = child_name if child != dynopt and child.startswith(dynopt):
for dynopt in dynopts: raise ConflictError(_(f'the option\'s name "{child}" start as '
if dynopt.could_conflict: f'the dynoptiondescription\'s name "{dynopt}"'))
continue old = child
for child in children:
if child != dynopt and dynopt.name_could_conflict(dynopt, child):
dynopt.could_conflict.append(weakref.ref(child))
child.could_conflict.append(weakref.ref(dynopt))
break
self._children = children_ self._children = children_
# 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 = None self._group_type = groups.default
self.impl_set_group_type(group_type)
def _setsubdyn( def _setsubdyn(self,
self, subdyn,
subdyn, ) -> None:
) -> None:
for child in self._children[1]: for child in self._children[1]:
child._setsubdyn(subdyn) child._setsubdyn(subdyn)
super()._setsubdyn(subdyn) super()._setsubdyn(subdyn)
def impl_is_optiondescription(self) -> bool: def impl_is_optiondescription(self) -> bool:
"""the option is an option description""" """the option is an option description
"""
return True return True
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
"""the option is not dynamic""" """the option is not dynamic
"""
return False return False
def impl_is_leadership(self) -> bool: def impl_is_leadership(self) -> bool:
"""the option is not a leadership""" """the option is not a leadership
"""
return False return False
# ____________________________________________________________ # ____________________________________________________________
def impl_set_group_type( def impl_set_group_type(self,
self, group_type: groups.GroupType,
group_type: groups.GroupType, ) -> None:
) -> None:
"""sets a given group object to an OptionDescription """sets a given group object to an OptionDescription
:param group_type: an instance of `GroupType` or `LeadershipGroupType` :param group_type: an instance of `GroupType` or `LeadershipGroupType`
that lives in `setting.groups` that lives in `setting.groups`
""" """
if __debug__: if __debug__:
if self._group_type is not None and self._group_type != groups.default: if self._group_type != groups.default:
raise ValueError( raise ValueError(_('cannot change group_type if already set '
_( '(old {0}, new {1})').format(self._group_type,
"cannot change group_type if already set " "(old {0}, new {1})" group_type))
).format(self._group_type, group_type)
)
if not isinstance(group_type, groups.GroupType): if not isinstance(group_type, groups.GroupType):
raise ValueError(_("group_type: {0}" " not allowed").format(group_type)) raise ValueError(_('group_type: {0}'
' not allowed').format(group_type))
if isinstance(group_type, groups.LeadershipGroupType): if isinstance(group_type, groups.LeadershipGroupType):
raise ConfigError( raise ConfigError('please use Leadership object instead of OptionDescription')
"please use Leadership object instead of OptionDescription"
)
self._group_type = group_type self._group_type = group_type
def impl_get_group_type(self) -> groups.GroupType: def impl_get_group_type(self) -> groups.GroupType:
"""get the group type of option description""" """get the group type of option description
"""
return self._group_type return self._group_type
def to_dynoption(self,
rootpath: str,
suffixes: Optional[list],
#ori_dyn,
) -> Union[SubDynOptionDescription, SynDynOptionDescription]:
"""get syn dyn option description
"""
if self.impl_is_dynoptiondescription():
obj = SynDynOptionDescription
else:
obj = SubDynOptionDescription
return obj(self,
rootpath,
suffixes,
)
def impl_is_dynsymlinkoption(self) -> bool: def impl_is_dynsymlinkoption(self) -> bool:
"""option is not a dyn symlink option""" """option is not a dyn symlink option
"""
return False return False

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,44 +18,15 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""PasswordOption""" """PasswordOption
"""
from ..i18n import _ from ..i18n import _
from ..error import display_list
from .stroption import StrOption from .stroption import StrOption
class PasswordOption(StrOption): class PasswordOption(StrOption):
"""represents the choice of a password""" """represents the choice of a password
"""
__slots__ = tuple() __slots__ = tuple()
_type = "password" _type = _('password')
_t_type = _("password")
_do_not_display_value_in_error = True
def __init__(self, *args, min_len=None, max_len=None, forbidden_char=[], **kwargs):
extra = {}
if min_len is not None:
extra["min_len"] = min_len
if max_len is not None:
extra["max_len"] = max_len
if forbidden_char:
extra["forbidden_char"] = set(forbidden_char)
super().__init__(*args, extra=extra, **kwargs)
def validate(self, value: str) -> None:
super().validate(value)
min_len = self.impl_get_extra("min_len")
if min_len and len(value) < min_len:
raise ValueError(_("at least {0} characters are required").format(min_len))
max_len = self.impl_get_extra("max_len")
if max_len and len(value) > max_len:
raise ValueError(_("maximum {0} characters required").format(max_len))
if self.impl_get_extra("forbidden_char"):
forbidden_char = set(value) & self.impl_get_extra("forbidden_char")
if forbidden_char:
raise ValueError(
_("must not have the characters {0}").format(
display_list(list(forbidden_char), add_quote=True)
)
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2023-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""PermissionsOption""" """PermissionsOption
"""
import re import re
from ..i18n import _ from ..i18n import _
@ -32,26 +33,27 @@ class PermissionsOption(IntOption):
If a fourth digit is present to the setuid bit, the setgid bit and the sticky bit attributes. If a fourth digit is present to the setuid bit, the setgid bit and the sticky bit attributes.
This option is an integer value. This option is an integer value.
""" """
__slots__ = tuple() __slots__ = tuple()
perm_re = re.compile(r"^[0-7]{3,4}$") perm_re = re.compile(r"^[0-7]{3,4}$")
_type = "unix file permissions" _type = _('unix file permissions')
_t_type = _("unix file permissions")
def __init__( def __init__(self,
self, *args,
*args, **kwargs,
**kwargs, ) -> None:
) -> None: #do not display intoption attributs
# do not display intoption attributs super().__init__(*args,
super().__init__(*args, **kwargs) **kwargs)
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
if not self.perm_re.search(str(value)): if not self.perm_re.search(str(value)):
raise ValueError(_("only 3 or 4 octal digits are allowed")) raise ValueError(_('only 3 or 4 octal digits are allowed'))
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
old_digit = 7 old_digit = 7
str_value = str(value) str_value = str(value)
if len(str_value) == 4: if len(str_value) == 4:
@ -60,12 +62,12 @@ class PermissionsOption(IntOption):
new_digit = int(digit) new_digit = int(digit)
if old_digit < new_digit: if old_digit < new_digit:
if idx == 1: if idx == 1:
old = _("user") old = _('user')
new = _("group") new = _('group')
else: else:
old = _("group") old = _('group')
new = _("other") new = _('other')
raise ValueError(_("{0} has more right than {1}").format(new, old)) raise ValueError(_(f'{new} has more right than {old}'))
old_digit = new_digit old_digit = new_digit
if str_value == "777": if str_value == '777':
raise ValueError(_("too weak")) raise ValueError(_('too weak'))

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,10 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""PortOption""" """PortOption
"""
import re
from ..i18n import _ from ..i18n import _
from .stroption import StrOption from .stroption import StrOption
@ -33,100 +36,85 @@ class PortOption(StrOption):
Port number 0 is reserved and can't be used. Port number 0 is reserved and can't be used.
see: http://en.wikipedia.org/wiki/Port_numbers see: http://en.wikipedia.org/wiki/Port_numbers
""" """
__slots__ = tuple() __slots__ = tuple()
_type = "port" port_re = re.compile(r"^[0-9]*$")
_t_type = _("port") _port = _('port')
def __init__( def __init__(self,
self, *args,
*args, allow_range: bool=False,
allow_range: bool = False, allow_zero: bool=False,
allow_zero: bool = False, allow_wellknown: bool=True,
allow_wellknown: bool = True, allow_registred: bool=True,
allow_registred: bool = True, allow_protocol: bool=False,
allow_protocol: bool = False, allow_private: bool=False,
allow_private: bool = False, **kwargs) -> None:
_extra: dict = None,
**kwargs, extra = {'_allow_range': allow_range,
) -> None: '_allow_protocol': allow_protocol,
if _extra is None: '_min_value': None,
extra = {} '_max_value': None,
else: }
extra = _extra
extra["allow_range"] = allow_range
extra["allow_protocol"] = allow_protocol
extra["allow_zero"] = allow_zero
extra["allow_wellknown"] = allow_wellknown
extra["allow_registred"] = allow_registred
extra["allow_private"] = allow_private
extra["_min_value"] = None
extra["_max_value"] = None
ports_min = [0, 1, 1024, 49152] ports_min = [0, 1, 1024, 49152]
ports_max = [0, 1023, 49151, 65535] ports_max = [0, 1023, 49151, 65535]
is_finally = False is_finally = False
for index, allowed in enumerate( for index, allowed in enumerate([allow_zero,
[allow_zero, allow_wellknown, allow_registred, allow_private] allow_wellknown,
): allow_registred,
if extra["_min_value"] is None: allow_private]):
if extra['_min_value'] is None:
if allowed: if allowed:
extra["_min_value"] = ports_min[index] extra['_min_value'] = ports_min[index]
elif not allowed: elif not allowed:
is_finally = True is_finally = True
elif allowed and is_finally: elif allowed and is_finally:
raise ValueError(_("inconsistency in allowed range")) raise ValueError(_('inconsistency in allowed range'))
if allowed: if allowed:
extra["_max_value"] = ports_max[index] extra['_max_value'] = ports_max[index]
if extra["_max_value"] is None: if extra['_max_value'] is None:
raise ValueError(_("max value is empty")) raise ValueError(_('max value is empty'))
super().__init__(*args, extra=extra, **kwargs) super().__init__(*args,
extra=extra,
**kwargs)
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
if self.impl_get_extra("allow_protocol") and ( if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
value.startswith("tcp:") or value.startswith("udp:") value.startswith('udp:')):
):
value = [value[4:]] value = [value[4:]]
elif self.impl_get_extra("allow_range") and ":" in str(value): elif self.impl_get_extra('_allow_range') and ":" in str(value):
value = value.split(":") value = value.split(':')
if len(value) != 2: if len(value) != 2:
raise ValueError(_("range must have two values only")) raise ValueError(_('range must have two values only'))
if not value[0] < value[1]: if not value[0] < value[1]:
raise ValueError( raise ValueError(_('first port in range must be'
_("first port in range must be" " smaller than the second one") ' smaller than the second one'))
)
else: else:
value = [value] value = [value]
for val in value: for val in value:
if not val.isdecimal(): if not self.port_re.search(val):
raise ValueError() raise ValueError()
def second_level_validation(self, value: str, warnings_only: bool) -> None: def second_level_validation(self,
if self.impl_get_extra("allow_protocol") and ( value: str,
value.startswith("tcp:") or value.startswith("udp:") warnings_only: bool) -> None:
): if self.impl_get_extra('_allow_protocol') and (value.startswith('tcp:') or
value.startswith('udp:')):
value = [value[4:]] value = [value[4:]]
elif ":" in value: elif ':' in value:
value = value.split(":") value = value.split(':')
else: else:
value = [value] value = [value]
for val in value: for val in value:
val = int(val) val = int(val)
if ( if not self.impl_get_extra('_min_value') <= val <= self.impl_get_extra('_max_value'):
not self.impl_get_extra("_min_value")
<= val
<= self.impl_get_extra("_max_value")
):
if warnings_only: if warnings_only:
msg = _("should be between {0} and {1}") msg = 'should be between {0} and {1}'
else: else:
msg = _("must be between {0} and {1}") msg = 'must be between {0} and {1}'
raise ValueError( raise ValueError(_(msg).format(self.impl_get_extra('_min_value'),
msg.format( self.impl_get_extra('_max_value')))
self.impl_get_extra("_min_value"),
self.impl_get_extra("_max_value"),
)
)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""StrOption and RegexpOption""" """StrOption and RegexpOption
"""
from typing import Any from typing import Any
from ..i18n import _ from ..i18n import _
@ -26,32 +27,31 @@ from .option import Option
class StrOption(Option): class StrOption(Option):
"""represents a string""" """represents a string
"""
__slots__ = tuple() __slots__ = tuple()
_type = "string" _type = _('string')
_t_type = _("string")
def validate( def validate(self,
self, value: str,
value: str, ) -> None:
) -> None: """validation
"""validation""" """
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_("it's not a string")) raise ValueError()
class RegexpOption(StrOption): class RegexpOption(StrOption):
"""regexp validation, this is base option use to do a custom's one""" """regexp validation, this is base option use to do a custom's one
"""
__slots__ = tuple() __slots__ = tuple()
def validate( def validate(self,
self, value: Any,
value: Any, ) -> None:
) -> None:
# pylint: disable=no-member # pylint: disable=no-member
"""validation""" """validation
"""
super().validate(value) super().validate(value)
match = self._regexp.search(value) match = self._regexp.search(value)
if not match: if not match:

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,81 +18,80 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""SymLinkOption link to an other option""" """SymLinkOption link to an other option
from typing import Any, Optional, Dict """
from typing import Any
from .baseoption import BaseOption, valid_name from .baseoption import BaseOption, valid_name
from ..error import ConfigError
from ..i18n import _ from ..i18n import _
class SymLinkOption(BaseOption): class SymLinkOption(BaseOption):
"""SymLinkOption link to an other option""" """SymLinkOption link to an other option
"""
__slots__ = ('_opt',)
__slots__ = ( def __init__(self,
"_opt", name: str,
"_leadership", opt: BaseOption,
) ) -> None:
def __init__(
self,
name: str,
opt: BaseOption,
) -> None:
# pylint: disable=super-init-not-called # pylint: disable=super-init-not-called
if not valid_name(name): if not valid_name(name):
raise ValueError(_('"{0}" is an invalid name for an option').format(name)) raise ValueError(_('"{0}" is an invalid name for an option').format(name))
if ( if not isinstance(opt, BaseOption) or \
not isinstance(opt, BaseOption) opt.impl_is_optiondescription() or \
or opt.impl_is_optiondescription() opt.impl_is_symlinkoption():
or opt.impl_is_symlinkoption() raise ValueError(_('malformed symlinkoption must be an option for symlink {0}'
): '').format(name))
raise ValueError( _setattr = object.__setattr__
_( _setattr(self, '_name', name)
'malformed symlink second parameters must be an option for "{0}", not {1}' _setattr(self, '_opt', opt)
).format(name, opt) opt._add_dependency(self)
)
self._name = name
self._opt = opt
self._leadership = None
opt._add_dependency(self, "symlink")
def __getattr__( def __getattr__(self,
self, name: str,
name: str, ) -> Any:
) -> Any: if name == '_path':
if name == "_subdyns":
return None
if name == "_path":
raise AttributeError() raise AttributeError()
return getattr(self._opt, name) return getattr(self._opt, name)
def _setsubdyn(self,
subdyn,
) -> None:
raise ConfigError(_('cannot set symlinkoption in a '
'dynoptiondescription'))
def impl_has_dependency(self,
self_is_dep: bool=True,
) -> bool:
"""If self_is_dep is True, it has dependency (self._opt), so return True
if self_is_dep is False, cannot has validation or callback, so return False
"""
return self_is_dep
def impl_is_symlinkoption(self) -> bool: def impl_is_symlinkoption(self) -> bool:
"""it's a symlinkoption""" """it's a symlinkoption
"""
return True return True
def impl_is_leader(self) -> bool:
return False
def impl_is_follower(self):
"""check if option is a leader in a follower"""
leadership = self._leadership
if leadership is None:
return False
return not leadership().is_leader(self)
def impl_getopt(self) -> BaseOption: def impl_getopt(self) -> BaseOption:
"""get to linked option""" """get to linked option
"""
return self._opt return self._opt
def impl_is_multi(self) -> bool: def issubdyn(self) -> bool:
"""is it a multi?""" """it's not a sub dyn option
if self._opt.impl_is_multi(): """
return True
if self._opt.issubdyn() or self.issubdyn():
if self.issubdyn() != self._opt.issubdyn():
return self._opt.issubdyn()
return self._opt.issubdyn() in self.get_sub_dyns()
return False return False
def impl_is_multi(self) -> bool:
"""is it a multi?
"""
if self._opt.issubdyn():
return True
return self._opt.impl_is_multi()
def impl_is_submulti(self) -> bool: def impl_is_submulti(self) -> bool:
"""is it a submulti?""" """is it a submulti?
"""
return self._opt.impl_is_submulti() return self._opt.impl_is_submulti()

View file

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-2023 Team tiramisu (see AUTHORS for all contributors)
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# The original `Config` design model is unproudly borrowed from
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
"""SynDynOption internal option, it's an instanciate synoption
"""
from typing import Any
from .baseoption import BaseOption
class SynDynOption:
"""SynDynOption is an Option include un DynOptionDescription with specified prefix
"""
__slots__ = ('rootpath',
'opt',
'suffixes',
'__weakref__')
def __init__(self,
opt: BaseOption,
rootpath: str,
suffixes: list,
) -> None:
self.opt = opt
self.rootpath = rootpath
self.suffixes = suffixes
def __getattr__(self,
name: str) -> Any:
return getattr(self.opt,
name,
)
def impl_getname(self) -> str:
"""get option name
"""
return self.opt.impl_getname()
def impl_get_display_name(self) -> str:
"""get option display name
"""
return self.opt.impl_get_display_name(self)
def get_suffixes(self) -> str:
"""get suffix
"""
return self.suffixes
def impl_getpath(self) -> str:
"""get path
"""
path = self.impl_getname()
if self.rootpath:
path = f'{self.rootpath}.{path}'
return path
def impl_is_dynsymlinkoption(self) -> bool:
"""it's a dynsymlinkoption
"""
return True
def impl_get_leadership(self): # pylint: disable=inconsistent-return-statements
"""is it a leadership?
"""
leadership = self.opt.impl_get_leadership()
if leadership:
rootpath = self.rootpath.rsplit('.', 1)[0]
return leadership.to_dynoption(rootpath,
self.suffixes,
)

View file

@ -0,0 +1,235 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# The original `Config` design model is unproudly borrowed from
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
"""SynDynOptionDescription and SynDynLeadership internal option
it's an instanciate synoptiondescription
"""
from typing import Optional, Iterator, Any, List
from ..i18n import _
from ..setting import ConfigBag, undefined
from .baseoption import BaseOption
from .syndynoption import SynDynOption
class Syn:
__slots__ = ('opt',
'rootpath',
'_suffixes',
)
def __init__(self,
opt: BaseOption,
rootpath: str,
suffixes: list,
) -> None:
self.opt = opt
self.rootpath = rootpath
self._suffixes = suffixes
def impl_get_display_name(self) -> str:
return self.opt.impl_get_display_name(self)
def get_child(self,
name: str,
config_bag: ConfigBag,
*,
allow_dynoption: bool=False,
):
"""get children
"""
# if not dyn
option = self.opt.get_child_not_dynamic(name,
allow_dynoption,
)
if option:
if allow_dynoption and option.impl_is_dynoptiondescription():
return option
return option.to_dynoption(self.impl_getpath(),
self._suffixes,
)
for child in self.opt._children[1]: # pylint: disable=no-member
if not child.impl_is_dynoptiondescription():
continue
for suffix in child.get_suffixes(config_bag,
dynoption=self,
):
if name != child.impl_getname(suffix):
continue
return child.to_dynoption(self.impl_getpath(),
self._suffixes + [suffix],
)
raise AttributeError(_(f'unknown option "{name}" '
f'in optiondescription "{self.impl_get_display_name()}"'
))
def get_children(self,
config_bag: ConfigBag,
):
# pylint: disable=unused-argument
"""get children
"""
for child in self.opt._children[1]:
if child.impl_is_dynoptiondescription():
for suffix in child.get_suffixes(config_bag,
dynoption=self,
):
yield child.to_dynoption(self.impl_getpath(),
self._suffixes + [suffix],
)
else:
yield child.to_dynoption(self.impl_getpath(),
self._suffixes,
)
def get_children_recursively(self,
bytype: Optional[BaseOption],
byname: Optional[str],
config_bag: ConfigBag,
self_opt: BaseOption=None,
) -> BaseOption:
# pylint: disable=unused-argument
"""get children recursively
"""
for option in self.opt.get_children_recursively(bytype,
byname,
config_bag,
self,
):
yield option
def get_suffixes(self) -> str:
"""get suffixes
"""
return self._suffixes
def impl_is_dynsymlinkoption(self) -> bool:
"""it's a dynsymlinkoption
"""
return True
class SubDynOptionDescription(Syn):
def impl_getpath(self) -> str:
"""get path
"""
path = self.opt.impl_getname()
if self.rootpath:
path = f'{self.rootpath}.{path}'
return path
def getsubdyn(self):
return self.opt.getsubdyn()
def impl_is_optiondescription(self):
return True
def impl_is_symlinkoption(self):
return False
def impl_is_leadership(self):
return False
def impl_is_dynoptiondescription(self) -> bool:
return True
def impl_getproperties(self):
return self.opt.impl_getproperties()
class SynDynOptionDescription(Syn):
"""SynDynOptionDescription internal option, it's an instanciate synoptiondescription
"""
def __getattr__(self,
name: str,
) -> Any:
# if not in SynDynOptionDescription, get value in self.opt
return getattr(self.opt,
name,
)
def impl_getname(self) -> str:
"""get name
"""
if self.opt.impl_is_dynoptiondescription():
return self.opt.impl_getname(self._suffixes[-1])
return self.opt.impl_getname()
def impl_getpath(self) -> str:
"""get path
"""
path = self.impl_getname()
if self.rootpath:
path = f'{self.rootpath}.{path}'
return path
def getsubdyn(self):
return self.opt
class SynDynLeadership(SynDynOptionDescription):
"""SynDynLeadership internal option, it's an instanciate synoptiondescription
"""
def get_leader(self) -> SynDynOption:
"""get the leader
"""
return self.opt.get_leader().to_dynoption(self.impl_getpath(),
self._suffixes,
)
def get_followers(self) -> Iterator[SynDynOption]:
"""get followers
"""
subpath = self.impl_getpath()
for follower in self.opt.get_followers():
yield follower.to_dynoption(subpath,
self._suffixes,
)
def pop(self,
*args,
**kwargs,
) -> None:
"""pop value for a follower
"""
self.opt.pop(*args,
followers=self.get_followers(),
**kwargs,
)
def follower_force_store_value(self,
value,
config_bag,
owner,
) -> None:
"""force store value for a follower
"""
self.opt.follower_force_store_value(value,
config_bag,
owner,
dyn=self,
)
def get_suffixes(self) -> str:
"""get suffix
"""
return self._suffixes

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""URLOption to check url value""" """URLOption to check url value
"""
import re import re
from typing import Any, Optional, List, Dict from typing import Any, Optional, List, Dict
@ -31,107 +32,100 @@ from .portoption import PortOption
class URLOption(StrOption): class URLOption(StrOption):
"""URLOption to check url value""" """URLOption to check url value
"""
__slots__ = tuple() __slots__ = tuple()
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
_type = "URL" _type = _('URL')
_t_type = _("URL")
def __init__( def __init__(self,
self, name: str,
name: str, doc: str,
doc: str, default: Any=undefined,
*args, default_multi: Any=None,
allow_ip: bool = False, multi: bool=False,
type: str = "domainname", validators: Optional[List[Calculation]]=None,
allow_without_dot=False, properties: Optional[List[str]]=None,
allow_range: bool = False, warnings_only: bool=False,
allow_zero: bool = False, extra: Optional[Dict]=None,
allow_wellknown: bool = True, allow_ip: bool=False,
allow_registred: bool = True, type: str='domainname',
allow_private: bool = False, allow_without_dot=False,
**kwargs, allow_range: bool=False,
) -> None: allow_zero: bool=False,
allow_wellknown: bool=True,
allow_registred: bool=True,
allow_private: bool=False) -> None:
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
extra = {} extra = {'_domainname': DomainnameOption(name,
extra["_domainname"] = DomainnameOption( doc,
name, allow_ip=allow_ip,
doc, type=type,
allow_ip=allow_ip, allow_without_dot=allow_without_dot),
type=type, '_port': PortOption(name,
allow_without_dot=allow_without_dot, doc,
_extra=extra, allow_range=allow_range,
) allow_zero=allow_zero,
extra["_port"] = PortOption( allow_wellknown=allow_wellknown,
name, allow_registred=allow_registred,
doc, allow_private=allow_private)}
allow_range=allow_range, super().__init__(name,
allow_zero=allow_zero, doc,
allow_wellknown=allow_wellknown, default=default,
allow_registred=allow_registred, default_multi=default_multi,
allow_private=allow_private, multi=multi,
_extra=extra, validators=validators,
) properties=properties,
super().__init__( warnings_only=warnings_only,
name, extra=extra)
doc,
extra=extra,
*args,
**kwargs,
)
def _get_domain_port_files(self, value: str) -> (str, str): def _get_domain_port_files(self,
if value.startswith("http://"): value: str) -> (str, str):
type_ = "http" if value.startswith('http://'):
type_ = 'http'
value = value[7:] value = value[7:]
elif value.startswith("https://"): elif value.startswith('https://'):
type_ = "https" type_ = 'https'
value = value[8:] value = value[8:]
else: else:
raise ValueError(_("must start with http:// or " "https://")) raise ValueError(_('must start with http:// or '
'https://'))
# get domain/files # get domain/files
splitted = value.split("/", 1) splitted = value.split('/', 1)
if len(splitted) == 1: if len(splitted) == 1:
domain = value domain = value
files = None files = None
else: else:
domain, files = splitted domain, files = splitted
# if port in domain # if port in domain
splitted = domain.split(":", 1) splitted = domain.split(':', 1)
if len(splitted) == 1: if len(splitted) == 1:
domain = splitted[0] domain = splitted[0]
port = {"http": "80", "https": "443"}[type_] port = {'http': '80',
'https': '443'}[type_]
else: else:
domain, port = splitted domain, port = splitted
return domain, port, files return domain, port, files
def validate(self, value: str) -> None: def validate(self,
value: str) -> None:
super().validate(value) super().validate(value)
domain, port, files = self._get_domain_port_files(value) domain, port, files = self._get_domain_port_files(value)
# validate port # validate port
portoption = self.impl_get_extra("_port") portoption = self.impl_get_extra('_port')
try: portoption.validate(port)
portoption.validate(port)
except ValueError as err:
msg = _('the port "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate domainname # validate domainname
domainnameoption = self.impl_get_extra("_domainname") domainnameoption = self.impl_get_extra('_domainname')
try: domainnameoption.validate(domain)
domainnameoption.validate(domain)
except ValueError as err:
msg = _('the domain "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate files # validate files
if files is not None and files != "" and not self.path_re.search(files): if files is not None and files != '' and not self.path_re.search(files):
raise ValueError(_("must ends with a valid resource name")) raise ValueError(_('must ends with a valid resource name'))
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
domain, port, _ = self._get_domain_port_files(value) domain, port, _ = self._get_domain_port_files(value)
# validate port # validate port
portoption = self.impl_get_extra("_port") portoption = self.impl_get_extra('_port')
portoption.second_level_validation(port, warnings_only) portoption.second_level_validation(port, warnings_only)
# validate domainname # validate domainname
domainnameoption = self.impl_get_extra("_domainname") domainnameoption = self.impl_get_extra('_domainname')
domainnameoption.second_level_validation(domain, warnings_only) domainnameoption.second_level_validation(domain, warnings_only)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017-2026 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2017-2023 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the # under the terms of the GNU Lesser General Public License as published by the
@ -18,7 +18,8 @@
# 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
# ____________________________________________________________ # ____________________________________________________________
"""UsernameOption or GroupnameOption to check unix username/group value""" """UsernameOption or GroupnameOption to check unix username/group value
"""
import re import re
from ..i18n import _ from ..i18n import _
@ -26,18 +27,16 @@ from .stroption import RegexpOption
class UsernameOption(RegexpOption): class UsernameOption(RegexpOption):
"""UsernameOption to check unix username value""" """UsernameOption to check unix username value
"""
__slots__ = tuple() __slots__ = tuple()
# regexp build with 'man 8 adduser' informations #regexp build with 'man 8 adduser' informations
_regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") _regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
_type = "unix username" _type = _('unix username')
_t_type = _("unix username")
class GroupnameOption(UsernameOption): class GroupnameOption(UsernameOption):
"""GroupnameOption to check unix group value""" """GroupnameOption to check unix group value
"""
__slots__ = tuple() __slots__ = tuple()
_type = "unix groupname" _type = _('unix groupname')
_t_type = _("unix groupname")

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff