970d3487d54458c83958dd3766af5dc33397c85d
[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, value, context, index, opts):
193         return getattr(self, func)(opt, value, context, index, opts)
194
195     def impl_validate(self, value, context=None, validate=True):
196         """
197         :param value: the option's value
198         :param validate: if true enables ``self._validator`` validation
199         """
200         def _val_validator(val):
201             callback_params = deepcopy(self._validator[1])
202             callback_params.setdefault('', []).insert(0, val)
203             return carry_out_calculation(self._name, config=context,
204                                          callback=self._validator[0],
205                                          callback_params=callback_params)
206
207         def val_validator():
208             #add current value has first argument
209             if self.impl_is_multi():
210                 for val in value:
211                     if not _val_validator(val):
212                         return False
213                 return True
214             else:
215                 return _val_validator(value)
216         # generic calculation
217         if context is not None:
218             descr = context.cfgimpl_get_description()
219         if not self._multi:
220             if value is not None and ((validate and
221                                       self._validator is not None and
222                                       not val_validator()) or
223                                       not self._validate(value)):
224                 raise ValueError(_("invalid value {0} for option {1}"
225                                    "").format(value, self._name))
226             if context is not None:
227                 descr._valid_consistency(self, value, context, None)
228         else:
229             if not isinstance(value, list):
230                 raise ValueError(_("invalid value {0} for option {1} "
231                                    "which must be a list").format(value,
232                                                                   self._name))
233             for index in range(0, len(value)):
234                 val = value[index]
235                 if val is not None and ((validate and
236                                          self._validator is not None and
237                                          not val_validator()) or
238                                         not self._validate(val)):
239                         raise ValueError(_("invalid value {0} for option {1}"
240                                            "").format(value, self._name))
241                 if context is not None:
242                     descr._valid_consistency(self, val, context, index)
243
244     def impl_getdefault(self, default_multi=False):
245         "accessing the default value"
246         if not default_multi or not self.impl_is_multi():
247             return self._default
248         else:
249             return self.getdefault_multi()
250
251     def impl_getdefault_multi(self):
252         "accessing the default value for a multi"
253         return self._default_multi
254
255     def impl_get_multitype(self):
256         return self._multitype
257
258     def impl_get_master_slaves(self):
259         return self._master_slaves
260
261     def impl_is_empty_by_default(self):
262         "no default value has been set yet"
263         if ((not self.impl_is_multi() and self._default is None) or
264                 (self.impl_is_multi() and (self._default == [] or None in self._default))):
265             return True
266         return False
267
268     def impl_getdoc(self):
269         "accesses the Option's doc"
270         return self.impl_get_information('doc')
271
272     def impl_has_callback(self):
273         "to know if a callback has been defined or not"
274         if self._callback is None:
275             return False
276         else:
277             return True
278
279     def impl_getkey(self, value):
280         return value
281
282     def impl_is_multi(self):
283         return self._multi
284
285     def impl_add_consistency(self, func, opts):
286         if self._consistencies is None:
287             self._consistencies = []
288         if self not in opts:
289             opts = list(opts)
290             opts.append(self)
291             opts = tuple(opts)
292         func = '_cons_{}'.format(func)
293         for opt in opts:
294             if opt != self:
295                 self._launch_consistency(func, opt, self.impl_getdefault(), None, None, opts)
296         self._consistencies.append((func, opts))
297         self.impl_validate(self.impl_getdefault(), None)
298
299     def _cons_not_equal(self, opt, value, context, index, opts):
300         values = [value]
301         if context is not None:
302             descr = context.cfgimpl_get_description()
303         for opt_ in opts:
304             if opt_ is not opt:
305                 if context is not None:
306                     path = descr.impl_get_path_by_opt(opt_)
307                     val = context._getattr(path, validate=False)
308                 else:
309                     val = opt.impl_getdefault()
310                 if val is not None:
311                     if val in values:
312                         raise ValueError(_("invalid value {0} for option {1} "
313                                            "must be different as {2} option"
314                                            "").format(val, self._name,
315                                                       opt_._name))
316                     values.append(val)
317
318
319 class ChoiceOption(Option):
320     __slots__ = ('_values', '_open_values', '_opt_type')
321     _opt_type = 'string'
322
323     def __init__(self, name, doc, values, default=None, default_multi=None,
324                  requires=None, multi=False, callback=None,
325                  callback_params=None, open_values=False, validator=None,
326                  validator_args=None, properties=()):
327         if not isinstance(values, tuple):
328             raise TypeError(_('values must be a tuple for {0}').format(name))
329         self._values = values
330         if open_values not in (True, False):
331             raise TypeError(_('open_values must be a boolean for '
332                             '{0}').format(name))
333         self._open_values = open_values
334         super(ChoiceOption, self).__init__(name, doc, default=default,
335                                            default_multi=default_multi,
336                                            callback=callback,
337                                            callback_params=callback_params,
338                                            requires=requires,
339                                            multi=multi,
340                                            validator=validator,
341                                            validator_args=validator_args,
342                                            properties=properties)
343
344     def impl_get_values(self):
345         return self._values
346
347     def impl_is_openvalues(self):
348         return self._open_values
349
350     def _validate(self, value):
351         if not self._open_values:
352             return value is None or value in self._values
353         else:
354             return True
355
356
357 class BoolOption(Option):
358     __slots__ = ('_opt_type',)
359     _opt_type = 'bool'
360
361     def _validate(self, value):
362         return isinstance(value, bool)
363
364
365 class IntOption(Option):
366     __slots__ = ('_opt_type',)
367     _opt_type = 'int'
368
369     def _validate(self, value):
370         return isinstance(value, int)
371
372
373 class FloatOption(Option):
374     __slots__ = ('_opt_type',)
375     _opt_type = 'float'
376
377     def _validate(self, value):
378         return isinstance(value, float)
379
380
381 class StrOption(Option):
382     __slots__ = ('_opt_type',)
383     _opt_type = 'string'
384
385     def _validate(self, value):
386         return isinstance(value, str)
387
388
389 class UnicodeOption(Option):
390     __slots__ = ('_opt_type',)
391     _opt_type = 'unicode'
392     _empty = u''
393
394     def _validate(self, value):
395         return isinstance(value, unicode)
396
397
398 class SymLinkOption(object):
399     __slots__ = ('_name', '_opt', '_consistencies')
400     _opt_type = 'symlink'
401     _consistencies = None
402
403     def __init__(self, name, opt):
404         self._name = name
405         if not isinstance(opt, Option):
406             raise ValueError(_('malformed symlinkoption '
407                                'must be an option for symlink {0}').format(name))
408         self._opt = opt
409
410     def __getattr__(self, name):
411         if name in ('_name', '_opt', '_consistencies'):
412             return object.__gettattr__(self, name)
413         else:
414             return getattr(self._opt, name)
415
416
417 class IPOption(Option):
418     __slots__ = ('_opt_type', '_only_private')
419     _opt_type = 'ip'
420
421     def __init__(self, name, doc, default=None, default_multi=None,
422                  requires=None, multi=False, callback=None,
423                  callback_params=None, validator=None, validator_args=None,
424                  properties=None, only_private=False):
425         super(IPOption, self).__init__(name, doc, default=default,
426                                        default_multi=default_multi,
427                                        callback=callback,
428                                        callback_params=callback_params,
429                                        requires=requires,
430                                        multi=multi,
431                                        validator=validator,
432                                        validator_args=validator_args,
433                                        properties=properties)
434         self._only_private = only_private
435
436     def _validate(self, value):
437         try:
438             ip = IP('{0}/32'.format(value))
439             if self._only_private:
440                 return ip.iptype() == 'PRIVATE'
441             return True
442         except ValueError:
443             return False
444
445
446 class NetworkOption(Option):
447     __slots__ = ('_opt_type',)
448     _opt_type = 'network'
449
450     def _validate(self, value):
451         try:
452             IP(value)
453             return True
454         except ValueError:
455             return False
456
457
458 class NetmaskOption(Option):
459     __slots__ = ('_opt_type',)
460     _opt_type = 'netmask'
461
462     def _validate(self, value):
463         try:
464             IP('0.0.0.0/{}'.format(value))
465             return True
466         except ValueError:
467             return False
468
469     def _cons_network_netmask(self, opt, value, context, index, opts):
470         #opts must be (netmask, network) options
471         self.__cons_netmask(opt, value, context, index, opts, False)
472
473     def _cons_ip_netmask(self, opt, value, context, index, opts):
474         #opts must be (netmask, ip) options
475         self.__cons_netmask(opt, value, context, index, opts, True)
476
477     def __cons_netmask(self, opt, value, context, index, opts, make_net):
478         opt_netmask, opt_ipnetwork = opts
479         if context is not None:
480             descr = context.cfgimpl_get_description()
481         if opt is opt_ipnetwork:
482             val_ipnetwork = value
483             if context is not None:
484                 path = descr.impl_get_path_by_opt(opt_netmask)
485                 val_netmask = context._getattr(path, validate=False)
486             else:
487                 val_netmask = opt_netmask.impl_getdefault()
488             if opt_netmask.impl_is_multi():
489                 val_netmask = val_netmask[index]
490         else:
491             val_netmask = value
492             if context is not None:
493                 path = descr.impl_get_path_by_opt(opt_ipnetwork)
494                 val_ipnetwork = context._getattr(path, validate=False)
495             else:
496                 val_ipnetwork = opt_ipnetwork.impl_getdefault()
497             if opt_ipnetwork.impl_is_multi():
498                 val_ipnetwork = val_ipnetwork[index]
499         if None not in (val_ipnetwork, val_netmask):
500             msg = None
501             try:
502                 ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
503                         make_net=make_net)
504                 #if cidr == 32, ip same has network
505                 if ip.prefixlen() != 32:
506                     try:
507                         IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
508                            make_net=not make_net)
509                     except ValueError:
510                         if not make_net:
511                             msg = _("invalid network {0} ({1}) with netmask {2} ({3}),"
512                                     " this network is an ip")
513                     else:
514                         if make_net:
515                             msg = _("invalid ip {0} ({1}) with netmask {2} ({3}),"
516                                     " this ip is a network")
517
518             except ValueError:
519                 if make_net:
520                     msg = _("invalid ip {0} ({1}) with netmask {2} ({3})")
521                 else:
522                     msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
523             if msg is not None:
524                 raise ValueError(msg.format(val_ipnetwork, opt_ipnetwork._name,
525                                             val_netmask, opt_netmask._name))
526
527
528 class DomainnameOption(Option):
529     __slots__ = ('_opt_type', '_type', '_allow_ip')
530     _opt_type = 'domainname'
531
532     def __init__(self, name, doc, default=None, default_multi=None,
533                  requires=None, multi=False, callback=None,
534                  callback_params=None, validator=None, validator_args=None,
535                  properties=None, allow_ip=False, type_='domainname'):
536         #netbios: for MS domain
537         #hostname: to identify the device
538         #domainname:
539         #fqdn: with tld, not supported yet
540         if type_ not in ['netbios', 'hostname', 'domainname']:
541             raise ValueError(_('unknown type_ {0} for hostname').format(type_))
542         self._type = type_
543         if allow_ip not in [True, False]:
544             raise ValueError(_('allow_ip must be a boolean'))
545         self._allow_ip = allow_ip
546         super(DomainnameOption, self).__init__(name, doc, default=default,
547                                                default_multi=default_multi,
548                                                callback=callback,
549                                                callback_params=callback_params,
550                                                requires=requires,
551                                                multi=multi,
552                                                validator=validator,
553                                                validator_args=validator_args,
554                                                properties=properties)
555
556     def _validate(self, value):
557         if self._allow_ip is True:
558             try:
559                 IP('{0}/32'.format(value))
560                 return True
561             except ValueError:
562                 pass
563         if self._type == 'netbios':
564             length = 15
565             extrachar = ''
566         elif self._type == 'hostname':
567             length = 63
568             extrachar = ''
569         elif self._type == 'domainname':
570             length = 255
571             extrachar = '\.'
572             if '.' not in value:
573                 raise ValueError(_("invalid value for {0}, must have dot"
574                                    "").format(self._name))
575         if len(value) > length:
576             raise ValueError(_("invalid value's length for {0} (max {1})"
577                                "").format(self._name, length))
578         regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
579         return re.match(regexp, value) is not None
580
581
582 class OptionDescription(BaseInformation):
583     """Config's schema (organisation, group) and container of Options"""
584     __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
585                  '_properties', '_children', '_consistencies')
586
587     def __init__(self, name, doc, children, requires=None, properties=None):
588         """
589         :param children: is a list of option descriptions (including
590         ``OptionDescription`` instances for nested namespaces).
591         """
592         if not valid_name(name):
593             raise ValueError(_("invalid name: {0} for option descr").format(name))
594         self._name = name
595         self._impl_informations = {}
596         self.impl_set_information('doc', doc)
597         child_names = [child._name for child in children]
598         #better performance like this
599         valid_child = copy(child_names)
600         valid_child.sort()
601         old = None
602         for child in valid_child:
603             if id(child) == id(old):
604                 raise ConflictError(_('duplicate option name: '
605                                       '{0}').format(child))
606             old = child
607         self._children = (tuple(child_names), tuple(children))
608         validate_requires_arg(requires, self._name)
609         self._requires = requires
610         self._cache_paths = None
611         self._consistencies = None
612         if properties is None:
613             properties = tuple()
614         if not isinstance(properties, tuple):
615             raise TypeError(_('invalid properties type {0} for {1},'
616                               ' must be a tuple').format(type(properties), self._name))
617         self._properties = set(properties)  # 'hidden', 'disabled'...
618         # the group_type is useful for filtering OptionDescriptions in a config
619         self._group_type = groups.default
620
621     def impl_getdoc(self):
622         return self.impl_get_information('doc')
623
624     def __getattr__(self, name):
625         try:
626             return self._children[1][self._children[0].index(name)]
627         except ValueError:
628             raise AttributeError(_('unknown Option {} in OptionDescription {}'
629                                  '').format(name, self._name))
630
631     def impl_getkey(self, config):
632         return tuple([child.impl_getkey(getattr(config, child._name))
633                       for child in self.impl_getchildren()])
634
635     def impl_getpaths(self, include_groups=False, _currpath=None):
636         """returns a list of all paths in self, recursively
637            _currpath should not be provided (helps with recursion)
638         """
639         if _currpath is None:
640             _currpath = []
641         paths = []
642         for option in self.impl_getchildren():
643             attr = option._name
644             if isinstance(option, OptionDescription):
645                 if include_groups:
646                     paths.append('.'.join(_currpath + [attr]))
647                 paths += option.impl_getpaths(include_groups=include_groups,
648                                               _currpath=_currpath + [attr])
649             else:
650                 paths.append('.'.join(_currpath + [attr]))
651         return paths
652
653     def impl_getchildren(self):
654         return self._children[1]
655
656     def impl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
657         if _currpath is None and self._cache_paths is not None:
658             return
659         if _currpath is None:
660             save = True
661             _currpath = []
662             _consistencies = {}
663         else:
664             save = False
665         if cache_path is None:
666             cache_path = [self._name]
667             cache_option = [self]
668         for option in self.impl_getchildren():
669             attr = option._name
670             if attr.startswith('_cfgimpl'):
671                 continue
672             cache_option.append(option)
673             cache_path.append(str('.'.join(_currpath + [attr])))
674             if not isinstance(option, OptionDescription):
675                 if option._consistencies is not None:
676                     for consistency in option._consistencies:
677                         func, opts = consistency
678                         for opt in opts:
679                             _consistencies.setdefault(opt, []).append((func, opts))
680             else:
681                 _currpath.append(attr)
682                 option.impl_build_cache(cache_path, cache_option, _currpath, _consistencies)
683                 _currpath.pop()
684         if save:
685             #valid no duplicated option
686             valid_child = copy(cache_option)
687             valid_child.sort()
688             old = None
689             for child in valid_child:
690                 if id(child) == id(old):
691                     raise ConflictError(_('duplicate option: '
692                                           '{0}').format(child))
693                 old = child
694             self._cache_paths = (tuple(cache_option), tuple(cache_path))
695             self._consistencies = _consistencies
696
697     def impl_get_opt_by_path(self, path):
698         try:
699             return self._cache_paths[0][self._cache_paths[1].index(path)]
700         except ValueError:
701             raise AttributeError(_('no option for path {}').format(path))
702
703     def impl_get_path_by_opt(self, opt):
704         try:
705             return self._cache_paths[1][self._cache_paths[0].index(opt)]
706         except ValueError:
707             raise AttributeError(_('no option {} found').format(opt))
708
709     # ____________________________________________________________
710     def impl_set_group_type(self, group_type):
711         """sets a given group object to an OptionDescription
712
713         :param group_type: an instance of `GroupType` or `MasterGroupType`
714                               that lives in `setting.groups`
715         """
716         if self._group_type != groups.default:
717             raise TypeError(_('cannot change group_type if already set '
718                             '(old {}, new {})').format(self._group_type, group_type))
719         if isinstance(group_type, groups.GroupType):
720             self._group_type = group_type
721             if isinstance(group_type, groups.MasterGroupType):
722                 #if master (same name has group) is set
723                 identical_master_child_name = False
724                 #for collect all slaves
725                 slaves = []
726                 master = None
727                 for child in self.impl_getchildren():
728                     if isinstance(child, OptionDescription):
729                         raise ValueError(_("master group {} shall not have "
730                                          "a subgroup").format(self._name))
731                     if not child.impl_is_multi():
732                         raise ValueError(_("not allowed option {0} in group {1}"
733                                          ": this option is not a multi"
734                                          "").format(child._name, self._name))
735                     if child._name == self._name:
736                         identical_master_child_name = True
737                         child._multitype = multitypes.master
738                         master = child
739                     else:
740                         slaves.append(child)
741                 if master is None:
742                     raise ValueError(_('master group with wrong master name for {}'
743                                      '').format(self._name))
744                 master._master_slaves = tuple(slaves)
745                 for child in self.impl_getchildren():
746                     if child != master:
747                         child._master_slaves = master
748                         child._multitype = multitypes.slave
749                 if not identical_master_child_name:
750                     raise ValueError(_("the master group: {} has not any "
751                                      "master child").format(self._name))
752         else:
753             raise ValueError(_('not allowed group_type : {0}').format(group_type))
754
755     def impl_get_group_type(self):
756         return self._group_type
757
758     def _valid_consistency(self, opt, value, context=None, index=None):
759         consistencies = self._consistencies.get(opt)
760         if consistencies is not None:
761             for consistency in consistencies:
762                 func, opts = consistency
763                 #ret = getattr(opts[0], func)(opt, value, context, index, opts)
764                 ret = opts[0]._launch_consistency(func, opt, value, context,
765                                                   index, opts)
766                 if ret is False:
767                     return False
768         return True
769
770
771 def validate_requires_arg(requires, name):
772     "check malformed requirements"
773     if requires is not None:
774         config_action = {}
775         for req in requires:
776             if not type(req) == tuple:
777                 raise ValueError(_("malformed requirements type for option:"
778                                    " {0}, must be a tuple").format(name))
779             if not isinstance(req[0], Option):
780                 raise ValueError(_('malformed requirements first argument '
781                                    'must be an option in option {0}').format(name))
782             if req[0].impl_is_multi():
783                 raise ValueError(_('malformed requirements option {0} '
784                                    'should not be a multi').format(name))
785             if not req[0]._validate(req[1]):
786                 raise ValueError(_('malformed requirements second argument '
787                                    'must be valid for option {0}').format(name))
788             if len(req) == 3:
789                 action = req[2]
790                 inverse = False
791             elif len(req) == 4:
792                 action = req[2]
793                 inverse = req[3]
794             else:
795                 raise ValueError(_("malformed requirements for option: {0}"
796                                  " invalid len").format(name))
797             if action in config_action:
798                 if inverse != config_action[action]:
799                     raise ValueError(_("inconsistency in action types for option: {0}"
800                                      " action: {1}").format(name, action))
801             else:
802                 config_action[action] = inverse