add option name's validation and rename Option method with objimpl_
[tiramisu.git] / tiramisu / option.py
index d905de5..650953d 100644 (file)
@@ -30,6 +30,8 @@ from tiramisu.setting import groups, multitypes
 from tiramisu.i18n import _
 
 name_regexp = re.compile(r'^\d+')
+forbidden_names = ['iter_all', 'iter_group', 'find', 'find_fisrt',
+                   'make_dict', 'unwrap_from_path']
 
 
 def valid_name(name):
@@ -37,7 +39,10 @@ def valid_name(name):
         name = str(name)
     except:
         return False
-    if re.match(name_regexp, name) is None:
+    if re.match(name_regexp, name) is None and not name.startswith('_') \
+            and name not in forbidden_names \
+            and not name.startswith('objimpl_') and \
+            not name.startswith('cfgimpl_'):
         return True
     else:
         return False
@@ -48,7 +53,7 @@ def valid_name(name):
 class BaseInformation(object):
     __slots__ = ('_informations')
 
-    def set_information(self, key, value):
+    def objimpl_set_information(self, key, value):
         """updates the information's attribute
         (wich is a dictionnary)
 
@@ -57,7 +62,7 @@ class BaseInformation(object):
         """
         self._informations[key] = value
 
-    def get_information(self, key, default=None):
+    def objimpl_get_information(self, key, default=None):
         """retrieves one information's item
 
         :param key: the item string (ex: "help")
@@ -85,7 +90,6 @@ class Option(BaseInformation):
                  requires=None, multi=False, callback=None,
                  callback_params=None, validator=None, validator_args=None,
                  properties=None):
-        #FIXME : validation de callback et callback_params !!!
         """
         :param name: the option's name
         :param doc: the option's description
@@ -107,7 +111,7 @@ class Option(BaseInformation):
             raise ValueError(_("invalid name: {0} for option").format(name))
         self._name = name
         self._informations = {}
-        self.set_information('doc', doc)
+        self.objimpl_set_information('doc', doc)
         validate_requires_arg(requires, self._name)
         self._requires = requires
         self._multi = multi
@@ -133,6 +137,11 @@ class Option(BaseInformation):
             raise ValueError(_("params defined for a callback function but "
                              "no callback defined yet for option {0}").format(name))
         if callback is not None:
+            if not isinstance(callback, str):
+                raise ValueError('callback must be a string')
+            if callback_params is not None and \
+                    not isinstance(callback_params, dict):
+                raise ValueError('callback_params must be a dict')
             self._callback = (callback, callback_params)
         else:
             self._callback = None
@@ -143,14 +152,14 @@ class Option(BaseInformation):
             #    raise ValidateError("invalid default value {0} "
             #                        "for option {1} : not list type"
             #                        "".format(str(default), name))
-            if not self.validate(default):
+            if not self.objimpl_validate(default):
                 raise ValueError(_("invalid default value {0} "
                                  "for option {1}"
                                  "").format(str(default), name))
             self._multitype = multitypes.default
             self._default_multi = default_multi
         else:
-            if default is not None and not self.validate(default):
+            if default is not None and not self.objimpl_validate(default):
                 raise ValueError(_("invalid default value {0} "
                                  "for option {1}").format(str(default), name))
         self._default = default
@@ -161,7 +170,7 @@ class Option(BaseInformation):
                             ' must be a tuple').format(type(properties), self._name))
         self._properties = properties  # 'hidden', 'disabled'...
 
-    def validate(self, value, context=None, validate=True):
+    def objimpl_validate(self, value, context=None, validate=True):
         """
         :param value: the option's value
         :param validate: if true enables ``self._validator`` validation
@@ -181,7 +190,7 @@ class Option(BaseInformation):
                 if not self._validate(value):
                     return False
                 if cons is not None:
-                    return cons.valid_consistency(self, value, context, None)
+                    return cons._valid_consistency(self, value, context, None)
         else:
             if not isinstance(value, list):
                 raise ValueError(_("invalid value {0} "
@@ -197,52 +206,52 @@ class Option(BaseInformation):
                         return False
                     if not self._validate(val):
                         return False
-                    if cons is not None and not cons.valid_consistency(self, val, context, index):
+                    if cons is not None and not cons._valid_consistency(self, val, context, index):
                         return False
         return True
 
-    def getdefault(self, default_multi=False):
+    def objimpl_getdefault(self, default_multi=False):
         "accessing the default value"
-        if not default_multi or not self.is_multi():
+        if not default_multi or not self.objimpl_is_multi():
             return self._default
         else:
             return self.getdefault_multi()
 
-    def getdefault_multi(self):
+    def objimpl_getdefault_multi(self):
         "accessing the default value for a multi"
         return self._default_multi
 
-    def get_multitype(self):
+    def objimpl_get_multitype(self):
         return self._multitype
 
-    def get_master_slaves(self):
+    def objimpl_get_master_slaves(self):
         return self._master_slaves
 
-    def is_empty_by_default(self):
+    def objimpl_is_empty_by_default(self):
         "no default value has been set yet"
-        if ((not self.is_multi() and self._default is None) or
-                (self.is_multi() and (self._default == [] or None in self._default))):
+        if ((not self.objimpl_is_multi() and self._default is None) or
+                (self.objimpl_is_multi() and (self._default == [] or None in self._default))):
             return True
         return False
 
-    def getdoc(self):
+    def objimpl_getdoc(self):
         "accesses the Option's doc"
-        return self.get_information('doc')
+        return self.objimpl_get_information('doc')
 
-    def has_callback(self):
+    def objimpl_has_callback(self):
         "to know if a callback has been defined or not"
         if self._callback is None:
             return False
         else:
             return True
 
-    def getkey(self, value):
+    def objimpl_getkey(self, value):
         return value
 
-    def is_multi(self):
+    def objimpl_is_multi(self):
         return self._multi
 
-    def add_consistency(self, func, opts):
+    def objimpl_add_consistency(self, func, opts):
         pass
         if self._consistencies is None:
             self._consistencies = []
@@ -250,14 +259,14 @@ class Option(BaseInformation):
             opts = list(opts)
             opts.append(self)
             opts = tuple(opts)
-        self._consistencies.append(('cons_{}'.format(func), opts))
+        self._consistencies.append(('_cons_{}'.format(func), opts))
 
-    def cons_not_equal(self, opt, value, context, index, opts):
+    def _cons_not_equal(self, opt, value, context, index, opts):
         values = [value]
         descr = context.cfgimpl_get_description()
         for opt_ in opts:
             if opt_ is not opt:
-                path = descr.get_path_by_opt(opt_)
+                path = descr.objimpl_get_path_by_opt(opt_)
                 val = context._getattr(path, validate=False)
                 if val is not None:
                     if val in values:
@@ -265,7 +274,7 @@ class Option(BaseInformation):
                     values.append(val)
         return True
 
-    def cons_lower(self, value):
+    def _cons_lower(self, value):
         try:
             return value.islower()
         except AttributeError:
@@ -274,8 +283,8 @@ class Option(BaseInformation):
 
 
 class ChoiceOption(Option):
-    __slots__ = ('_values', '_open_values', 'opt_type')
-    opt_type = 'string'
+    __slots__ = ('_values', '_open_values', '_opt_type')
+    _opt_type = 'string'
 
     def __init__(self, name, doc, values, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
@@ -306,40 +315,40 @@ class ChoiceOption(Option):
 
 
 class BoolOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'bool'
+    __slots__ = ('_opt_type')
+    _opt_type = 'bool'
 
     def _validate(self, value):
         return isinstance(value, bool)
 
 
 class IntOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'int'
+    __slots__ = ('_opt_type')
+    _opt_type = 'int'
 
     def _validate(self, value):
         return isinstance(value, int)
 
 
 class FloatOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'float'
+    __slots__ = ('_opt_type')
+    _opt_type = 'float'
 
     def _validate(self, value):
         return isinstance(value, float)
 
 
 class StrOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'string'
+    __slots__ = ('_opt_type')
+    _opt_type = 'string'
 
     def _validate(self, value):
         return isinstance(value, str)
 
 
 class UnicodeOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'unicode'
+    __slots__ = ('_opt_type')
+    _opt_type = 'unicode'
     _empty = u''
 
     def _validate(self, value):
@@ -347,30 +356,30 @@ class UnicodeOption(Option):
 
 
 class SymLinkOption(object):
-    __slots__ = ('_name', 'opt', '_consistencies')
-    opt_type = 'symlink'
+    __slots__ = ('_name', '_opt', '_consistencies')
+    _opt_type = 'symlink'
     _consistencies = None
 
     def __init__(self, name, path, opt):
         self._name = name
-        self.opt = opt
+        self._opt = opt
 
-    def setoption(self, context, value):
-        path = context.cfgimpl_get_description().get_path_by_opt(self.opt)
+    def _setoption(self, context, value):
+        path = context.cfgimpl_get_description().objimpl_get_path_by_opt(self._opt)
         setattr(context, path, value)
 
     def __getattr__(self, name):
-        if name in ('_name', 'opt', 'setoption'):
+        if name in ('_name', '_opt', '_setoption', '_consistencies'):
             return object.__gettattr__(self, name)
         else:
-            return getattr(self.opt, name)
+            return getattr(self._opt, name)
 
 
 class IPOption(Option):
-    __slots__ = ('opt_type', '_only_private')
-    opt_type = 'ip'
+    __slots__ = ('_opt_type', '_only_private')
+    _opt_type = 'ip'
 
-    def set_private(self):
+    def objimpl_set_private(self):
         self._only_private = True
 
     def _validate(self, value):
@@ -388,8 +397,8 @@ class IPOption(Option):
 
 
 class NetworkOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'network'
+    __slots__ = ('_opt_type')
+    _opt_type = 'network'
 
     def _validate(self, value):
         try:
@@ -400,8 +409,8 @@ class NetworkOption(Option):
 
 
 class NetmaskOption(Option):
-    __slots__ = ('opt_type')
-    opt_type = 'netmask'
+    __slots__ = ('_opt_type')
+    _opt_type = 'netmask'
 
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
@@ -435,30 +444,30 @@ class NetmaskOption(Option):
         except ValueError:
             return False
 
-    def cons_network_netmask(self, opt, value, context, index, opts):
+    def _cons_network_netmask(self, opt, value, context, index, opts):
         #opts must be (netmask, network) options
         return self._cons_netmask(opt, value, context, index, opts, False)
 
-    def cons_ip_netmask(self, opt, value, context, index, opts):
+    def _cons_ip_netmask(self, opt, value, context, index, opts):
         #opts must be (netmask, ip) options
         return self._cons_netmask(opt, value, context, index, opts, True)
 
-    def _cons_netmask(self, opt, value, context, index, opts, make_net):
+    def __cons_netmask(self, opt, value, context, index, opts, make_net):
         opt_netmask, opt_ipnetwork = opts
         descr = context.cfgimpl_get_description()
         if opt is opt_ipnetwork:
             val_ipnetwork = value
-            path = descr.get_path_by_opt(opt_netmask)
+            path = descr.objimpl_get_path_by_opt(opt_netmask)
             val_netmask = context._getattr(path, validate=False)
-            if opt_netmask.is_multi():
+            if opt_netmask.objimpl_is_multi():
                 val_netmask = val_netmask[index]
             if val_netmask is None:
                 return True
         else:
             val_netmask = value
-            path = descr.get_path_by_opt(opt_ipnetwork)
+            path = descr.objimpl_get_path_by_opt(opt_ipnetwork)
             val_ipnetwork = getattr(context, path)
-            if opt_ipnetwork.is_multi():
+            if opt_ipnetwork.objimpl_is_multi():
                 val_ipnetwork = val_ipnetwork[index]
             if val_ipnetwork is None:
                 return True
@@ -470,8 +479,8 @@ class NetmaskOption(Option):
 
 
 class DomainnameOption(Option):
-    __slots__ = ('opt_type', '_type', '_allow_ip')
-    opt_type = 'domainname'
+    __slots__ = ('_opt_type', '_type', '_allow_ip')
+    _opt_type = 'domainname'
     #allow_ip
 
     def __init__(self, name, doc, default=None, default_multi=None,
@@ -531,7 +540,7 @@ class OptionDescription(BaseInformation):
             raise ValueError(_("invalid name: {0} for option descr").format(name))
         self._name = name
         self._informations = {}
-        self.set_information('doc', doc)
+        self.objimpl_set_information('doc', doc)
         child_names = [child._name for child in children]
         #better performance like this
         valid_child = copy(child_names)
@@ -554,8 +563,8 @@ class OptionDescription(BaseInformation):
         # the group_type is useful for filtering OptionDescriptions in a config
         self._group_type = groups.default
 
-    def getdoc(self):
-        return self.get_information('doc')
+    def objimpl_getdoc(self):
+        return self.objimpl_get_information('doc')
 
     def __getattr__(self, name):
         try:
@@ -564,33 +573,32 @@ class OptionDescription(BaseInformation):
             raise AttributeError(_('unknown Option {} in OptionDescription {}'
                                  '').format(name, self._name))
 
-    def getkey(self, config):
-        return tuple([child.getkey(getattr(config, child._name))
-                      for child in self._children[1]])
+    def objimpl_getkey(self, config):
+        return tuple([child.objimpl_getkey(getattr(config, child._name))
+                      for child in self.objimpl_getchildren()])
 
-    def getpaths(self, include_groups=False, _currpath=None):
+    def objimpl_getpaths(self, include_groups=False, _currpath=None):
         """returns a list of all paths in self, recursively
            _currpath should not be provided (helps with recursion)
         """
-        #FIXME : cache
         if _currpath is None:
             _currpath = []
         paths = []
-        for option in self._children[1]:
+        for option in self.objimpl_getchildren():
             attr = option._name
             if isinstance(option, OptionDescription):
                 if include_groups:
                     paths.append('.'.join(_currpath + [attr]))
-                paths += option.getpaths(include_groups=include_groups,
-                                         _currpath=_currpath + [attr])
+                paths += option.objimpl_getpaths(include_groups=include_groups,
+                                                 _currpath=_currpath + [attr])
             else:
                 paths.append('.'.join(_currpath + [attr]))
         return paths
 
-    def getchildren(self):
+    def objimpl_getchildren(self):
         return self._children[1]
 
-    def build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
+    def objimpl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
         if _currpath is None and self._cache_paths is not None:
             return
         if _currpath is None:
@@ -602,7 +610,7 @@ class OptionDescription(BaseInformation):
         if cache_path is None:
             cache_path = [self._name]
             cache_option = [self]
-        for option in self._children[1]:
+        for option in self.objimpl_getchildren():
             attr = option._name
             if attr.startswith('_cfgimpl'):
                 continue
@@ -616,7 +624,7 @@ class OptionDescription(BaseInformation):
                             _consistencies.setdefault(opt, []).append((func, opts))
             else:
                 _currpath.append(attr)
-                option.build_cache(cache_path, cache_option, _currpath, _consistencies)
+                option.objimpl_build_cache(cache_path, cache_option, _currpath, _consistencies)
                 _currpath.pop()
         if save:
             #valid no duplicated option
@@ -631,20 +639,20 @@ class OptionDescription(BaseInformation):
             self._cache_paths = (tuple(cache_option), tuple(cache_path))
             self._consistencies = _consistencies
 
-    def get_opt_by_path(self, path):
+    def obgimpl_get_opt_by_path(self, path):
         try:
             return self._cache_paths[0][self._cache_paths[1].index(path)]
         except ValueError:
             raise AttributeError(_('no option for path {}').format(path))
 
-    def get_path_by_opt(self, opt):
+    def objimpl_get_path_by_opt(self, opt):
         try:
             return self._cache_paths[1][self._cache_paths[0].index(opt)]
         except ValueError:
             raise AttributeError(_('no option {} found').format(opt))
 
     # ____________________________________________________________
-    def set_group_type(self, group_type):
+    def objimpl_set_group_type(self, group_type):
         """sets a given group object to an OptionDescription
 
         :param group_type: an instance of `GroupType` or `MasterGroupType`
@@ -661,11 +669,11 @@ class OptionDescription(BaseInformation):
                 #for collect all slaves
                 slaves = []
                 master = None
-                for child in self._children[1]:
+                for child in self.objimpl_getchildren():
                     if isinstance(child, OptionDescription):
                         raise ValueError(_("master group {} shall not have "
                                          "a subgroup").format(self._name))
-                    if not child.is_multi():
+                    if not child.objimpl_is_multi():
                         raise ValueError(_("not allowed option {0} in group {1}"
                                          ": this option is not a multi"
                                          "").format(child._name, self._name))
@@ -679,7 +687,7 @@ class OptionDescription(BaseInformation):
                     raise ValueError(_('master group with wrong master name for {}'
                                      '').format(self._name))
                 master._master_slaves = tuple(slaves)
-                for child in self._children[1]:
+                for child in self.objimpl_getchildren():
                     if child != master:
                         child._master_slaves = master
                         child._multitype = multitypes.slave
@@ -689,10 +697,10 @@ class OptionDescription(BaseInformation):
         else:
             raise ValueError(_('not allowed group_type : {0}').format(group_type))
 
-    def get_group_type(self):
+    def objimpl_get_group_type(self):
         return self._group_type
 
-    def valid_consistency(self, opt, value, context, index):
+    def _valid_consistency(self, opt, value, context, index):
         consistencies = self._consistencies.get(opt)
         if consistencies is not None:
             for consistency in consistencies: