multi, None and validation
[tiramisu.git] / tiramisu / option.py
1 # -*- coding: utf-8 -*-
2 "option types and option description for the configuration management"
3 # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 #
19 # The original `Config` design model is unproudly borrowed from
20 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
21 # the whole pypy projet is under MIT licence
22 # ____________________________________________________________
23 import re
24 from copy import copy, deepcopy
25 from types import FunctionType
26 from IPy import IP
27
28 from tiramisu.error import ConflictError
29 from tiramisu.setting import groups, multitypes
30 from tiramisu.i18n import _
31 from tiramisu.autolib import carry_out_calculation
32
33 name_regexp = re.compile(r'^\d+')
34 forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
35                    'make_dict', 'unwrap_from_path', 'read_only',
36                    'read_write', 'getowner', 'set_contexts')
37
38
39 def valid_name(name):
40     try:
41         name = str(name)
42     except:
43         return False
44     if re.match(name_regexp, name) is None and not name.startswith('_') \
45             and name not in forbidden_names \
46             and not name.startswith('impl_') \
47             and not name.startswith('cfgimpl_'):
48         return True
49     else:
50         return False
51 #____________________________________________________________
52 #
53
54
55 class BaseInformation(object):
56     __slots__ = ('_impl_informations',)
57
58     def impl_set_information(self, key, value):
59         """updates the information's attribute
60         (wich is a dictionnary)
61
62         :param key: information's key (ex: "help", "doc"
63         :param value: information's value (ex: "the help string")
64         """
65         self._impl_informations[key] = value
66
67     def impl_get_information(self, key, default=None):
68         """retrieves one information's item
69
70         :param key: the item string (ex: "help")
71         """
72         if key in self._impl_informations:
73             return self._impl_informations[key]
74         elif default is not None:
75             return default
76         else:
77             raise ValueError(_("Information's item not found: {0}").format(key))
78
79
80 class Option(BaseInformation):
81     """
82     Abstract base class for configuration option's.
83
84     Reminder: an Option object is **not** a container for the value
85     """
86     __slots__ = ('_name', '_requires', '_multi', '_validator', '_default_multi',
87                  '_default', '_properties', '_callback', '_multitype',
88                  '_master_slaves', '_consistencies', '_empty')
89     _empty = ''
90
91     def __init__(self, name, doc, default=None, default_multi=None,
92                  requires=None, multi=False, callback=None,
93                  callback_params=None, validator=None, validator_args=None,
94                  properties=None):
95         """
96         :param name: the option's name
97         :param doc: the option's description
98         :param default: specifies the default value of the option,
99                         for a multi : ['bla', 'bla', 'bla']
100         :param default_multi: 'bla' (used in case of a reset to default only at
101                         a given index)
102         :param requires: is a list of names of options located anywhere
103                          in the configuration.
104         :param multi: if true, the option's value is a list
105         :param callback: the name of a function. If set, the function's output
106                          is responsible of the option's value
107         :param callback_params: the callback's parameter
108         :param validator: the name of a function wich stands for a custom
109                           validation of the value
110         :param validator_args: the validator's parameters
111         """
112         if not valid_name(name):
113             raise ValueError(_("invalid name: {0} for option").format(name))
114         self._name = name
115         self._impl_informations = {}
116         self.impl_set_information('doc', doc)
117         validate_requires_arg(requires, self._name)
118         self._requires = requires
119         self._multi = multi
120         self._consistencies = None
121         if validator is not None:
122             if type(validator) != FunctionType:
123                 raise TypeError(_("validator must be a function"))
124             if validator_args is None:
125                 validator_args = {}
126             self._validator = (validator, validator_args)
127         else:
128             self._validator = None
129         if not self._multi and default_multi is not None:
130             raise ValueError(_("a default_multi is set whereas multi is False"
131                              " in option: {0}").format(name))
132         if default_multi is not None and not self._validate(default_multi):
133             raise ValueError(_("invalid default_multi value {0} "
134                              "for option {1}").format(str(default_multi), name))
135         if callback is not None and (default is not None or default_multi is not None):
136             raise ValueError(_("defaut values not allowed if option: {0} "
137                              "is calculated").format(name))
138         if callback is None and callback_params is not None:
139             raise ValueError(_("params defined for a callback function but "
140                              "no callback defined yet for option {0}").format(name))
141         if callback is not None:
142             if type(callback) != FunctionType:
143                 raise ValueError('callback must be a function')
144             if callback_params is not None and \
145                     not isinstance(callback_params, dict):
146                 raise ValueError('callback_params must be a dict')
147             self._callback = (callback, callback_params)
148         else:
149             self._callback = None
150         if self._multi:
151             if default is None:
152                 default = []
153             self._multitype = multitypes.default
154             self._default_multi = default_multi
155         self.impl_validate(default)
156         self._default = default
157         if properties is None:
158             properties = tuple()
159         if not isinstance(properties, tuple):
160             raise TypeError(_('invalid properties type {0} for {1},'
161                             ' must be a tuple').format(type(properties), self._name))
162         self._properties = set(properties)  # 'hidden', 'disabled'...
163
164     def __eq__(self, other):
165         "Option comparison"
166         if not isinstance(other, Option):
167             return False
168         slots = list(self.__slots__ + Option.__slots__ + BaseInformation.__slots__)
169         for var in slots:
170             try:
171                 val1 = getattr(self, var)
172                 not_in1 = False
173             except:
174                 not_in1 = True
175             try:
176                 val2 = getattr(other, var)
177                 not_in2 = False
178             except:
179                 not_in2 = True
180             if True in (not_in1, not_in2):
181                 if not_in1 != not_in2:
182                     return False
183             elif val1 != val2:
184                 return False
185         return True
186
187     def __ne__(self, other):
188         if not isinstance(other, Option):
189             return False
190         return not self == other
191
192     def _launch_consistency(self, func, opt, vals, context, index, opt_):
193         if context is not None:
194             descr = context.cfgimpl_get_description()
195         if opt is self:
196             #values are for self, search opt_ values
197             values = vals
198             if context is not None:
199                 path = descr.impl_get_path_by_opt(opt_)
200                 values_ = context._getattr(path, validate=False)
201             else:
202                 values_ = opt_.impl_getdefault()
203             if index is not None:
204                 #value is not already set, could be higher
205                 try:
206                     values_ = values_[index]
207                 except IndexError:
208                     values_ = None
209         else:
210             #values are for opt_, search self values
211             values_ = vals
212             if context is not None:
213                 path = descr.impl_get_path_by_opt(self)
214                 values = context._getattr(path, validate=False)
215             else:
216                 values = self.impl_getdefault()
217             if index is not None:
218                 #value is not already set, could be higher
219                 try:
220                     values = values[index]
221                 except IndexError:
222                     values = None
223         if index is None and self.impl_is_multi():
224             for index in range(0, len(values)):
225                 try:
226                     value = values[index]
227                     value_ = values_[index]
228                 except IndexError:
229                     value = None
230                     value_ = None
231                 if None not in (value, value_):
232                     getattr(self, func)(opt_._name, value, value_)
233         else:
234             if None not in (values, values_):
235                 getattr(self, func)(opt_._name, values, values_)
236
237     def impl_validate(self, value, context=None, validate=True):
238         """
239         :param value: the option's value
240         :param validate: if true enables ``self._validator`` validation
241         """
242         if not validate:
243             return
244
245         def _val_validator(val):
246             callback_params = deepcopy(self._validator[1])
247             callback_params.setdefault('', []).insert(0, val)
248             return carry_out_calculation(self._name, config=context,
249                                          callback=self._validator[0],
250                                          callback_params=callback_params)
251
252         def val_validator():
253             #add current value has first argument
254             if self.impl_is_multi():
255                 for val in value:
256                     if not _val_validator(val):
257                         return False
258                 return True
259             else:
260                 return _val_validator(value)
261
262         # generic calculation
263         if context is not None:
264             descr = context.cfgimpl_get_description()
265         if not self._multi:
266             if value is not None and ((self._validator is not None and
267                                        not val_validator()) or
268                                       not self._validate(value)):
269                 raise ValueError(_("invalid value {0} for option {1}"
270                                    "").format(value, self._name))
271             if context is not None:
272                 descr._valid_consistency(self, value, context, None)
273         else:
274             if not isinstance(value, list):
275                 raise ValueError(_("invalid value {0} for option {1} "
276                                    "which must be a list").format(value,
277                                                                   self._name))
278             for index in range(0, len(value)):
279                 val = value[index]
280                 if val is not None and ((self._validator is not None and
281                                          not val_validator()) or
282                                         not self._validate(val)):
283                         raise ValueError(_("invalid value {0} for option {1}"
284                                            "").format(value, self._name))
285                 if context is not None:
286                     descr._valid_consistency(self, val, context, index)
287
288     def impl_getdefault(self, default_multi=False):
289         "accessing the default value"
290         if not default_multi or not self.impl_is_multi():
291             return self._default
292         else:
293             return self.getdefault_multi()
294
295     def impl_getdefault_multi(self):
296         "accessing the default value for a multi"
297         return self._default_multi
298
299     def impl_get_multitype(self):
300         return self._multitype
301
302     def impl_get_master_slaves(self):
303         return self._master_slaves
304
305     def impl_is_empty_by_default(self):
306         "no default value has been set yet"
307         if ((not self.impl_is_multi() and self._default is None) or
308                 (self.impl_is_multi() and (self._default == [] or None in self._default))):
309             return True
310         return False
311
312     def impl_getdoc(self):
313         "accesses the Option's doc"
314         return self.impl_get_information('doc')
315
316     def impl_has_callback(self):
317         "to know if a callback has been defined or not"
318         if self._callback is None:
319             return False
320         else:
321             return True
322
323     def impl_getkey(self, value):
324         return value
325
326     def impl_is_multi(self):
327         return self._multi
328
329     def impl_add_consistency(self, func, opt):
330         if self._consistencies is None:
331             self._consistencies = []
332         if not isinstance(opt, Option):
333             raise ValueError('consistency must be set with an option')
334         if self is opt:
335             raise ValueError('cannot add consistency with itself')
336         if self.impl_is_multi() != opt.impl_is_multi():
337             raise ValueError('options in consistency should be multi in two sides')
338         func = '_cons_{}'.format(func)
339         self._launch_consistency(func, self, self.impl_getdefault(), None, None, opt)
340         self._consistencies.append((func, opt))
341         self.impl_validate(self.impl_getdefault())
342
343     def _cons_not_equal(self, optname, value, value_):
344         if value == value_:
345             raise ValueError(_("invalid value {0} for option {1} "
346                                "must be different as {2} option"
347                                "").format(value, self._name, optname))
348
349
350 class ChoiceOption(Option):
351     __slots__ = ('_values', '_open_values', '_opt_type')
352     _opt_type = 'string'
353
354     def __init__(self, name, doc, values, default=None, default_multi=None,
355                  requires=None, multi=False, callback=None,
356                  callback_params=None, open_values=False, validator=None,
357                  validator_args=None, properties=()):
358         if not isinstance(values, tuple):
359             raise TypeError(_('values must be a tuple for {0}').format(name))
360         self._values = values
361         if open_values not in (True, False):
362             raise TypeError(_('open_values must be a boolean for '
363                             '{0}').format(name))
364         self._open_values = open_values
365         super(ChoiceOption, self).__init__(name, doc, default=default,
366                                            default_multi=default_multi,
367                                            callback=callback,
368                                            callback_params=callback_params,
369                                            requires=requires,
370                                            multi=multi,
371                                            validator=validator,
372                                            validator_args=validator_args,
373                                            properties=properties)
374
375     def impl_get_values(self):
376         return self._values
377
378     def impl_is_openvalues(self):
379         return self._open_values
380
381     def _validate(self, value):
382         if not self._open_values:
383             return value is None or value in self._values
384         else:
385             return True
386
387
388 class BoolOption(Option):
389     __slots__ = ('_opt_type',)
390     _opt_type = 'bool'
391
392     def _validate(self, value):
393         return isinstance(value, bool)
394
395
396 class IntOption(Option):
397     __slots__ = ('_opt_type',)
398     _opt_type = 'int'
399
400     def _validate(self, value):
401         return isinstance(value, int)
402
403
404 class FloatOption(Option):
405     __slots__ = ('_opt_type',)
406     _opt_type = 'float'
407
408     def _validate(self, value):
409         return isinstance(value, float)
410
411
412 class StrOption(Option):
413     __slots__ = ('_opt_type',)
414     _opt_type = 'string'
415
416     def _validate(self, value):
417         return isinstance(value, str)
418
419
420 class UnicodeOption(Option):
421     __slots__ = ('_opt_type',)
422     _opt_type = 'unicode'
423     _empty = u''
424
425     def _validate(self, value):
426         return isinstance(value, unicode)
427
428
429 class SymLinkOption(object):
430     __slots__ = ('_name', '_opt', '_consistencies')
431     _opt_type = 'symlink'
432     _consistencies = None
433
434     def __init__(self, name, opt):
435         self._name = name
436         if not isinstance(opt, Option):
437             raise ValueError(_('malformed symlinkoption '
438                                'must be an option for symlink {0}').format(name))
439         self._opt = opt
440
441     def __getattr__(self, name):
442         if name in ('_name', '_opt', '_consistencies'):
443             return object.__gettattr__(self, name)
444         else:
445             return getattr(self._opt, name)
446
447
448 class IPOption(Option):
449     __slots__ = ('_opt_type', '_only_private')
450     _opt_type = 'ip'
451
452     def __init__(self, name, doc, default=None, default_multi=None,
453                  requires=None, multi=False, callback=None,
454                  callback_params=None, validator=None, validator_args=None,
455                  properties=None, only_private=False):
456         super(IPOption, self).__init__(name, doc, default=default,
457                                        default_multi=default_multi,
458                                        callback=callback,
459                                        callback_params=callback_params,
460                                        requires=requires,
461                                        multi=multi,
462                                        validator=validator,
463                                        validator_args=validator_args,
464                                        properties=properties)
465         self._only_private = only_private
466
467     def _validate(self, value):
468         try:
469             ip = IP('{0}/32'.format(value))
470             if self._only_private:
471                 return ip.iptype() == 'PRIVATE'
472             return True
473         except ValueError:
474             return False
475
476
477 class NetworkOption(Option):
478     __slots__ = ('_opt_type',)
479     _opt_type = 'network'
480
481     def _validate(self, value):
482         try:
483             IP(value)
484             return True
485         except ValueError:
486             return False
487
488
489 class NetmaskOption(Option):
490     __slots__ = ('_opt_type',)
491     _opt_type = 'netmask'
492
493     def _validate(self, value):
494         try:
495             IP('0.0.0.0/{}'.format(value))
496             return True
497         except ValueError:
498             return False
499
500     def _cons_network_netmask(self, optname, value, value_):
501         #opts must be (netmask, network) options
502         self.__cons_netmask(optname, value, value_, False)
503
504     def _cons_ip_netmask(self, optname, value, value_):
505         #opts must be (netmask, ip) options
506         self.__cons_netmask(optname, value, value_, True)
507
508     #def __cons_netmask(self, opt, value, context, index, opts, make_net):
509     def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net):
510         msg = None
511         try:
512             ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
513                     make_net=make_net)
514             #if cidr == 32, ip same has network
515             if ip.prefixlen() != 32:
516                 try:
517                     IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
518                         make_net=not make_net)
519                 except ValueError:
520                     if not make_net:
521                         msg = _("invalid network {0} ({1}) with netmask {2} ({3}),"
522                                 " this network is an ip")
523                 else:
524                     if make_net:
525                         msg = _("invalid ip {0} ({1}) with netmask {2} ({3}),"
526                                 " this ip is a network")
527
528         except ValueError:
529             if make_net:
530                 msg = _("invalid ip {0} ({1}) with netmask {2} ({3})")
531             else:
532                 msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
533         if msg is not None:
534             raise ValueError(msg.format(val_ipnetwork, optname,
535                                         val_netmask, self._name))
536
537
538 class DomainnameOption(Option):
539     __slots__ = ('_opt_type', '_type', '_allow_ip')
540     _opt_type = 'domainname'
541
542     def __init__(self, name, doc, default=None, default_multi=None,
543                  requires=None, multi=False, callback=None,
544                  callback_params=None, validator=None, validator_args=None,
545                  properties=None, allow_ip=False, type_='domainname'):
546         #netbios: for MS domain
547         #hostname: to identify the device
548         #domainname:
549         #fqdn: with tld, not supported yet
550         if type_ not in ['netbios', 'hostname', 'domainname']:
551             raise ValueError(_('unknown type_ {0} for hostname').format(type_))
552         self._type = type_
553         if allow_ip not in [True, False]:
554             raise ValueError(_('allow_ip must be a boolean'))
555         self._allow_ip = allow_ip
556         super(DomainnameOption, self).__init__(name, doc, default=default,
557                                                default_multi=default_multi,
558                                                callback=callback,
559                                                callback_params=callback_params,
560                                                requires=requires,
561                                                multi=multi,
562                                                validator=validator,
563                                                validator_args=validator_args,
564                                                properties=properties)
565
566     def _validate(self, value):
567         if self._allow_ip is True:
568             try:
569                 IP('{0}/32'.format(value))
570                 return True
571             except ValueError:
572                 pass
573         if self._type == 'netbios':
574             length = 15
575             extrachar = ''
576         elif self._type == 'hostname':
577             length = 63
578             extrachar = ''
579         elif self._type == 'domainname':
580             length = 255
581             extrachar = '\.'
582             if '.' not in value:
583                 raise ValueError(_("invalid value for {0}, must have dot"
584                                    "").format(self._name))
585         if len(value) > length:
586             raise ValueError(_("invalid value's length for {0} (max {1})"
587                                "").format(self._name, length))
588         regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
589         return re.match(regexp, value) is not None
590
591
592 class OptionDescription(BaseInformation):
593     """Config's schema (organisation, group) and container of Options"""
594     __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
595                  '_properties', '_children', '_consistencies')
596
597     def __init__(self, name, doc, children, requires=None, properties=None):
598         """
599         :param children: is a list of option descriptions (including
600         ``OptionDescription`` instances for nested namespaces).
601         """
602         if not valid_name(name):
603             raise ValueError(_("invalid name: {0} for option descr").format(name))
604         self._name = name
605         self._impl_informations = {}
606         self.impl_set_information('doc', doc)
607         child_names = [child._name for child in children]
608         #better performance like this
609         valid_child = copy(child_names)
610         valid_child.sort()
611         old = None
612         for child in valid_child:
613             if id(child) == id(old):
614                 raise ConflictError(_('duplicate option name: '
615                                       '{0}').format(child))
616             old = child
617         self._children = (tuple(child_names), tuple(children))
618         validate_requires_arg(requires, self._name)
619         self._requires = requires
620         self._cache_paths = None
621         self._consistencies = None
622         if properties is None:
623             properties = tuple()
624         if not isinstance(properties, tuple):
625             raise TypeError(_('invalid properties type {0} for {1},'
626                               ' must be a tuple').format(type(properties), self._name))
627         self._properties = set(properties)  # 'hidden', 'disabled'...
628         # the group_type is useful for filtering OptionDescriptions in a config
629         self._group_type = groups.default
630
631     def impl_getdoc(self):
632         return self.impl_get_information('doc')
633
634     def __getattr__(self, name):
635         try:
636             return self._children[1][self._children[0].index(name)]
637         except ValueError:
638             raise AttributeError(_('unknown Option {} in OptionDescription {}'
639                                  '').format(name, self._name))
640
641     def impl_getkey(self, config):
642         return tuple([child.impl_getkey(getattr(config, child._name))
643                       for child in self.impl_getchildren()])
644
645     def impl_getpaths(self, include_groups=False, _currpath=None):
646         """returns a list of all paths in self, recursively
647            _currpath should not be provided (helps with recursion)
648         """
649         if _currpath is None:
650             _currpath = []
651         paths = []
652         for option in self.impl_getchildren():
653             attr = option._name
654             if isinstance(option, OptionDescription):
655                 if include_groups:
656                     paths.append('.'.join(_currpath + [attr]))
657                 paths += option.impl_getpaths(include_groups=include_groups,
658                                               _currpath=_currpath + [attr])
659             else:
660                 paths.append('.'.join(_currpath + [attr]))
661         return paths
662
663     def impl_getchildren(self):
664         return self._children[1]
665
666     def impl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
667         if _currpath is None and self._cache_paths is not None:
668             return
669         if _currpath is None:
670             save = True
671             _currpath = []
672             _consistencies = {}
673         else:
674             save = False
675         if cache_path is None:
676             cache_path = [self._name]
677             cache_option = [self]
678         for option in self.impl_getchildren():
679             attr = option._name
680             if attr.startswith('_cfgimpl'):
681                 continue
682             cache_option.append(option)
683             cache_path.append(str('.'.join(_currpath + [attr])))
684             if not isinstance(option, OptionDescription):
685                 if option._consistencies is not None:
686                     for consistency in option._consistencies:
687                         func, opt = consistency
688                         opts = (option, opt)
689                         _consistencies.setdefault(opt, []).append((func, opts))
690                         _consistencies.setdefault(option, []).append((func, opts))
691             else:
692                 _currpath.append(attr)
693                 option.impl_build_cache(cache_path, cache_option, _currpath, _consistencies)
694                 _currpath.pop()
695         if save:
696             #valid no duplicated option
697             valid_child = copy(cache_option)
698             valid_child.sort()
699             old = None
700             for child in valid_child:
701                 if id(child) == id(old):
702                     raise ConflictError(_('duplicate option: '
703                                           '{0}').format(child))
704                 old = child
705             self._cache_paths = (tuple(cache_option), tuple(cache_path))
706             self._consistencies = _consistencies
707
708     def impl_get_opt_by_path(self, path):
709         try:
710             return self._cache_paths[0][self._cache_paths[1].index(path)]
711         except ValueError:
712             raise AttributeError(_('no option for path {}').format(path))
713
714     def impl_get_path_by_opt(self, opt):
715         try:
716             return self._cache_paths[1][self._cache_paths[0].index(opt)]
717         except ValueError:
718             raise AttributeError(_('no option {} found').format(opt))
719
720     # ____________________________________________________________
721     def impl_set_group_type(self, group_type):
722         """sets a given group object to an OptionDescription
723
724         :param group_type: an instance of `GroupType` or `MasterGroupType`
725                               that lives in `setting.groups`
726         """
727         if self._group_type != groups.default:
728             raise TypeError(_('cannot change group_type if already set '
729                             '(old {}, new {})').format(self._group_type, group_type))
730         if isinstance(group_type, groups.GroupType):
731             self._group_type = group_type
732             if isinstance(group_type, groups.MasterGroupType):
733                 #if master (same name has group) is set
734                 identical_master_child_name = False
735                 #for collect all slaves
736                 slaves = []
737                 master = None
738                 for child in self.impl_getchildren():
739                     if isinstance(child, OptionDescription):
740                         raise ValueError(_("master group {} shall not have "
741                                          "a subgroup").format(self._name))
742                     if not child.impl_is_multi():
743                         raise ValueError(_("not allowed option {0} in group {1}"
744                                          ": this option is not a multi"
745                                          "").format(child._name, self._name))
746                     if child._name == self._name:
747                         identical_master_child_name = True
748                         child._multitype = multitypes.master
749                         master = child
750                     else:
751                         slaves.append(child)
752                 if master is None:
753                     raise ValueError(_('master group with wrong master name for {}'
754                                      '').format(self._name))
755                 master._master_slaves = tuple(slaves)
756                 for child in self.impl_getchildren():
757                     if child != master:
758                         child._master_slaves = master
759                         child._multitype = multitypes.slave
760                 if not identical_master_child_name:
761                     raise ValueError(_("the master group: {} has not any "
762                                      "master child").format(self._name))
763         else:
764             raise ValueError(_('not allowed group_type : {0}').format(group_type))
765
766     def impl_get_group_type(self):
767         return self._group_type
768
769     def _valid_consistency(self, opt, value, context=None, index=None):
770         consistencies = self._consistencies.get(opt)
771         if consistencies is not None:
772             for consistency in consistencies:
773                 opt_ = consistency[1]
774                 ret = opt_[0]._launch_consistency(consistency[0], opt, value, context,
775                                                   index, opt_[1])
776                 if ret is False:
777                     return False
778         return True
779
780
781 def validate_requires_arg(requires, name):
782     "check malformed requirements"
783     if requires is not None:
784         config_action = {}
785         for req in requires:
786             if not type(req) == tuple:
787                 raise ValueError(_("malformed requirements type for option:"
788                                    " {0}, must be a tuple").format(name))
789             if not isinstance(req[0], Option):
790                 raise ValueError(_('malformed requirements first argument '
791                                    'must be an option in option {0}').format(name))
792             if req[0].impl_is_multi():
793                 raise ValueError(_('malformed requirements option {0} '
794                                    'should not be a multi').format(name))
795             if not req[0]._validate(req[1]):
796                 raise ValueError(_('malformed requirements second argument '
797                                    'must be valid for option {0}').format(name))
798             if len(req) == 3:
799                 action = req[2]
800                 inverse = False
801             elif len(req) == 4:
802                 action = req[2]
803                 inverse = req[3]
804             else:
805                 raise ValueError(_("malformed requirements for option: {0}"
806                                  " invalid len").format(name))
807             if action in config_action:
808                 if inverse != config_action[action]:
809                     raise ValueError(_("inconsistency in action types for option: {0}"
810                                      " action: {1}").format(name, action))
811             else:
812                 config_action[action] = inverse