* config herite from BaseInformation class
[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     #allow_ip
532
533     def __init__(self, name, doc, default=None, default_multi=None,
534                  requires=None, multi=False, callback=None,
535                  callback_params=None, validator=None, validator_args=None,
536                  properties=None, allow_ip=False, type_='domainname'):
537         #netbios: for MS domain
538         #hostname: to identify the device
539         #domainname:
540         #fqdn: with tld, not supported yet
541         super(DomainnameOption, self).__init__(name, doc, default=default,
542                                                default_multi=default_multi,
543                                                callback=callback,
544                                                callback_params=callback_params,
545                                                requires=requires,
546                                                multi=multi,
547                                                validator=validator,
548                                                validator_args=validator_args,
549                                                properties=properties)
550         if type_ not in ['netbios', 'hostname', 'domainname']:
551             raise ValueError(_('unknown type_ {0} for hostname').format(type_))
552         self._type = type_
553         self._allow_ip = allow_ip
554
555     def _validate(self, value):
556         if self._allow_ip is True:
557             try:
558                 IP('{0}/32'.format(value))
559                 return True
560             except ValueError:
561                 pass
562         if self._type == 'netbios':
563             length = 15
564             extrachar = ''
565         elif self._type == 'hostname':
566             length = 63
567             extrachar = ''
568         elif self._type == 'domainname':
569             length = 255
570             extrachar = '\.'
571             if '.' not in value:
572                 raise ValueError(_("invalid value for {0}, must have dot"
573                                    "").format(self._name))
574         if len(value) > length:
575             raise ValueError(_("invalid value's length for {0} (max {1})"
576                                "").format(self._name, length))
577         regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
578         return re.match(regexp, value) is not None
579
580
581 class OptionDescription(BaseInformation):
582     """Config's schema (organisation, group) and container of Options"""
583     __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
584                  '_properties', '_children', '_consistencies')
585
586     def __init__(self, name, doc, children, requires=None, properties=None):
587         """
588         :param children: is a list of option descriptions (including
589         ``OptionDescription`` instances for nested namespaces).
590         """
591         if not valid_name(name):
592             raise ValueError(_("invalid name: {0} for option descr").format(name))
593         self._name = name
594         self._impl_informations = {}
595         self.impl_set_information('doc', doc)
596         child_names = [child._name for child in children]
597         #better performance like this
598         valid_child = copy(child_names)
599         valid_child.sort()
600         old = None
601         for child in valid_child:
602             if id(child) == id(old):
603                 raise ConflictError(_('duplicate option name: '
604                                       '{0}').format(child))
605             old = child
606         self._children = (tuple(child_names), tuple(children))
607         validate_requires_arg(requires, self._name)
608         self._requires = requires
609         self._cache_paths = None
610         self._consistencies = None
611         if properties is None:
612             properties = tuple()
613         if not isinstance(properties, tuple):
614             raise TypeError(_('invalid properties type {0} for {1},'
615                               ' must be a tuple').format(type(properties), self._name))
616         self._properties = set(properties)  # 'hidden', 'disabled'...
617         # the group_type is useful for filtering OptionDescriptions in a config
618         self._group_type = groups.default
619
620     def impl_getdoc(self):
621         return self.impl_get_information('doc')
622
623     def __getattr__(self, name):
624         try:
625             return self._children[1][self._children[0].index(name)]
626         except ValueError:
627             raise AttributeError(_('unknown Option {} in OptionDescription {}'
628                                  '').format(name, self._name))
629
630     def impl_getkey(self, config):
631         return tuple([child.impl_getkey(getattr(config, child._name))
632                       for child in self.impl_getchildren()])
633
634     def impl_getpaths(self, include_groups=False, _currpath=None):
635         """returns a list of all paths in self, recursively
636            _currpath should not be provided (helps with recursion)
637         """
638         if _currpath is None:
639             _currpath = []
640         paths = []
641         for option in self.impl_getchildren():
642             attr = option._name
643             if isinstance(option, OptionDescription):
644                 if include_groups:
645                     paths.append('.'.join(_currpath + [attr]))
646                 paths += option.impl_getpaths(include_groups=include_groups,
647                                               _currpath=_currpath + [attr])
648             else:
649                 paths.append('.'.join(_currpath + [attr]))
650         return paths
651
652     def impl_getchildren(self):
653         return self._children[1]
654
655     def impl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
656         if _currpath is None and self._cache_paths is not None:
657             return
658         if _currpath is None:
659             save = True
660             _currpath = []
661             _consistencies = {}
662         else:
663             save = False
664         if cache_path is None:
665             cache_path = [self._name]
666             cache_option = [self]
667         for option in self.impl_getchildren():
668             attr = option._name
669             if attr.startswith('_cfgimpl'):
670                 continue
671             cache_option.append(option)
672             cache_path.append(str('.'.join(_currpath + [attr])))
673             if not isinstance(option, OptionDescription):
674                 if option._consistencies is not None:
675                     for consistency in option._consistencies:
676                         func, opts = consistency
677                         for opt in opts:
678                             _consistencies.setdefault(opt, []).append((func, opts))
679             else:
680                 _currpath.append(attr)
681                 option.impl_build_cache(cache_path, cache_option, _currpath, _consistencies)
682                 _currpath.pop()
683         if save:
684             #valid no duplicated option
685             valid_child = copy(cache_option)
686             valid_child.sort()
687             old = None
688             for child in valid_child:
689                 if id(child) == id(old):
690                     raise ConflictError(_('duplicate option: '
691                                           '{0}').format(child))
692                 old = child
693             self._cache_paths = (tuple(cache_option), tuple(cache_path))
694             self._consistencies = _consistencies
695
696     def impl_get_opt_by_path(self, path):
697         try:
698             return self._cache_paths[0][self._cache_paths[1].index(path)]
699         except ValueError:
700             raise AttributeError(_('no option for path {}').format(path))
701
702     def impl_get_path_by_opt(self, opt):
703         try:
704             return self._cache_paths[1][self._cache_paths[0].index(opt)]
705         except ValueError:
706             raise AttributeError(_('no option {} found').format(opt))
707
708     # ____________________________________________________________
709     def impl_set_group_type(self, group_type):
710         """sets a given group object to an OptionDescription
711
712         :param group_type: an instance of `GroupType` or `MasterGroupType`
713                               that lives in `setting.groups`
714         """
715         if self._group_type != groups.default:
716             raise TypeError(_('cannot change group_type if already set '
717                             '(old {}, new {})').format(self._group_type, group_type))
718         if isinstance(group_type, groups.GroupType):
719             self._group_type = group_type
720             if isinstance(group_type, groups.MasterGroupType):
721                 #if master (same name has group) is set
722                 identical_master_child_name = False
723                 #for collect all slaves
724                 slaves = []
725                 master = None
726                 for child in self.impl_getchildren():
727                     if isinstance(child, OptionDescription):
728                         raise ValueError(_("master group {} shall not have "
729                                          "a subgroup").format(self._name))
730                     if not child.impl_is_multi():
731                         raise ValueError(_("not allowed option {0} in group {1}"
732                                          ": this option is not a multi"
733                                          "").format(child._name, self._name))
734                     if child._name == self._name:
735                         identical_master_child_name = True
736                         child._multitype = multitypes.master
737                         master = child
738                     else:
739                         slaves.append(child)
740                 if master is None:
741                     raise ValueError(_('master group with wrong master name for {}'
742                                      '').format(self._name))
743                 master._master_slaves = tuple(slaves)
744                 for child in self.impl_getchildren():
745                     if child != master:
746                         child._master_slaves = master
747                         child._multitype = multitypes.slave
748                 if not identical_master_child_name:
749                     raise ValueError(_("the master group: {} has not any "
750                                      "master child").format(self._name))
751         else:
752             raise ValueError(_('not allowed group_type : {0}').format(group_type))
753
754     def impl_get_group_type(self):
755         return self._group_type
756
757     def _valid_consistency(self, opt, value, context=None, index=None):
758         consistencies = self._consistencies.get(opt)
759         if consistencies is not None:
760             for consistency in consistencies:
761                 func, opts = consistency
762                 #ret = getattr(opts[0], func)(opt, value, context, index, opts)
763                 ret = opts[0]._launch_consistency(func, opt, value, context,
764                                                   index, opts)
765                 if ret is False:
766                     return False
767         return True
768
769
770 def validate_requires_arg(requires, name):
771     "check malformed requirements"
772     if requires is not None:
773         config_action = {}
774         for req in requires:
775             if not type(req) == tuple:
776                 raise ValueError(_("malformed requirements type for option:"
777                                    " {0}, must be a tuple").format(name))
778             if not isinstance(req[0], Option):
779                 raise ValueError(_('malformed requirements first argument '
780                                    'must be an option in option {0}').format(name))
781             if req[0].impl_is_multi():
782                 raise ValueError(_('malformed requirements option {0} '
783                                    'should not be a multi').format(name))
784             if not req[0]._validate(req[1]):
785                 raise ValueError(_('malformed requirements second argument '
786                                    'must be valid for option {0}').format(name))
787             if len(req) == 3:
788                 action = req[2]
789                 inverse = False
790             elif len(req) == 4:
791                 action = req[2]
792                 inverse = req[3]
793             else:
794                 raise ValueError(_("malformed requirements for option: {0}"
795                                  " invalid len").format(name))
796             if action in config_action:
797                 if inverse != config_action[action]:
798                     raise ValueError(_("inconsistency in action types for option: {0}"
799                                      " action: {1}").format(name, action))
800             else:
801                 config_action[action] = inverse