From 3c9a759e2e130bbc2fe0cf0fc0e570548266758b Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 24 Jul 2017 19:04:18 +0200 Subject: [PATCH] separate option in differents files --- tiramisu/option/__init__.py | 22 +- tiramisu/option/booloption.py | 34 ++ tiramisu/option/broadcastoption.py | 55 +++ tiramisu/option/choiceoption.py | 98 +++++ tiramisu/option/dateoption.py | 40 ++ tiramisu/option/domainnameoption.py | 133 ++++++ tiramisu/option/emailoption.py | 31 ++ tiramisu/option/filenameoption.py | 30 ++ tiramisu/option/floatoption.py | 34 ++ tiramisu/option/intoption.py | 34 ++ tiramisu/option/ipoption.py | 96 +++++ tiramisu/option/netmaskoption.py | 80 ++++ tiramisu/option/networkoption.py | 54 +++ tiramisu/option/option.py | 604 +--------------------------- tiramisu/option/passwordoption.py | 35 ++ tiramisu/option/portoption.py | 110 +++++ tiramisu/option/stroption.py | 52 +++ tiramisu/option/urloption.py | 73 ++++ tiramisu/option/usernameoption.py | 31 ++ 19 files changed, 1039 insertions(+), 607 deletions(-) create mode 100644 tiramisu/option/booloption.py create mode 100644 tiramisu/option/broadcastoption.py create mode 100644 tiramisu/option/choiceoption.py create mode 100644 tiramisu/option/dateoption.py create mode 100644 tiramisu/option/domainnameoption.py create mode 100644 tiramisu/option/emailoption.py create mode 100644 tiramisu/option/filenameoption.py create mode 100644 tiramisu/option/floatoption.py create mode 100644 tiramisu/option/intoption.py create mode 100644 tiramisu/option/ipoption.py create mode 100644 tiramisu/option/netmaskoption.py create mode 100644 tiramisu/option/networkoption.py create mode 100644 tiramisu/option/passwordoption.py create mode 100644 tiramisu/option/portoption.py create mode 100644 tiramisu/option/stroption.py create mode 100644 tiramisu/option/urloption.py create mode 100644 tiramisu/option/usernameoption.py diff --git a/tiramisu/option/__init__.py b/tiramisu/option/__init__.py index a11cb64..2437d3d 100644 --- a/tiramisu/option/__init__.py +++ b/tiramisu/option/__init__.py @@ -2,11 +2,23 @@ from .masterslave import MasterSlaves from .optiondescription import OptionDescription, DynOptionDescription, \ SynDynOptionDescription from .baseoption import Option, SymLinkOption, DynSymLinkOption, submulti -from .option import (ChoiceOption, BoolOption, IntOption, FloatOption, - StrOption, UnicodeOption, IPOption, PortOption, - NetworkOption, NetmaskOption, BroadcastOption, - DomainnameOption, EmailOption, URLOption, UsernameOption, - DateOption, FilenameOption, PasswordOption) +from .choiceoption import ChoiceOption +from .booloption import BoolOption +from .intoption import IntOption +from .floatoption import FloatOption +from .stroption import StrOption, UnicodeOption +from .ipoption import IPOption +from .portoption import PortOption +from .networkoption import NetworkOption +from .netmaskoption import NetmaskOption +from .broadcastoption import BroadcastOption +from .domainnameoption import DomainnameOption +from .emailoption import EmailOption +from .urloption import URLOption +from .usernameoption import UsernameOption +from .dateoption import DateOption +from .filenameoption import FilenameOption +from .passwordoption import PasswordOption __all__ = ('MasterSlaves', 'OptionDescription', 'DynOptionDescription', diff --git a/tiramisu/option/booloption.py b/tiramisu/option/booloption.py new file mode 100644 index 0000000..ed6581f --- /dev/null +++ b/tiramisu/option/booloption.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class BoolOption(Option): + "represents a choice between ``True`` and ``False``" + __slots__ = tuple() + _display_name = _('boolean') + + def _validate(self, value, context=undefined, current_opt=undefined): + if not isinstance(value, bool): + return ValueError() diff --git a/tiramisu/option/broadcastoption.py b/tiramisu/option/broadcastoption.py new file mode 100644 index 0000000..175fd0e --- /dev/null +++ b/tiramisu/option/broadcastoption.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +from IPy import IP + +from ..error import ConfigError +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class BroadcastOption(Option): + __slots__ = tuple() + _display_name = _('broadcast address') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err + if value.count('.') != 3: + return ValueError() + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + return ValueError() + try: + IP('{0}/32'.format(value)) + except ValueError: + return ValueError() + + def _cons_broadcast(self, current_opt, opts, vals, warnings_only): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + if None in vals: + return + broadcast, network, netmask = vals + if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): + return ValueError(_('broadcast {4} invalid with network {0}/{1} ({2}/{3})').format( + network, netmask, opts[1].impl_getname(), opts[2].impl_getname(), broadcast)) diff --git a/tiramisu/option/choiceoption.py b/tiramisu/option/choiceoption.py new file mode 100644 index 0000000..eeffc53 --- /dev/null +++ b/tiramisu/option/choiceoption.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +from types import FunctionType + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option, validate_callback, display_list +from ..autolib import carry_out_calculation +from ..error import ConfigError + + +class ChoiceOption(Option): + """represents a choice out of several objects. + + The option can also have the value ``None`` + """ + __slots__ = tuple() + _display_name = _('choice') + + def __init__(self, name, doc, values, default=None, + values_params=None, default_multi=None, requires=None, + multi=False, callback=None, callback_params=None, + validator=None, validator_params=None, + properties=None, warnings_only=False): + """ + :param values: is a list of values the option can possibly take + """ + if isinstance(values, FunctionType): + validate_callback(values, values_params, 'values', self) + else: + if values_params is not None: + raise ValueError(_('values is not a function, so values_params must be None')) + if not isinstance(values, tuple): + raise TypeError(_('values must be a tuple or a function for {0}' + ).format(name)) + self._choice_values = values + if values_params is not None: + self._choice_values_params = values_params + super(ChoiceOption, self).__init__(name, doc, default=default, + default_multi=default_multi, + callback=callback, + callback_params=callback_params, + requires=requires, + multi=multi, + validator=validator, + validator_params=validator_params, + properties=properties, + warnings_only=warnings_only) + + def impl_get_values(self, context, current_opt=undefined): + if current_opt is undefined: + current_opt = self + #FIXME cache? but in context... + values = self._choice_values + if isinstance(values, FunctionType): + if context is None: + values = [] + else: + values = carry_out_calculation(current_opt, context=context, + callback=values, + callback_params=getattr(self, '_choice_values_params', {})) + if isinstance(values, Exception): + return values + if values is not undefined and not isinstance(values, list): + raise ConfigError(_('calculated values for {0} is not a list' + '').format(self.impl_getname())) + return values + + + def _validate(self, value, context=undefined, current_opt=undefined): + values = self.impl_get_values(context, current_opt=current_opt) + if isinstance(values, Exception): + return values + if values is not undefined and not value in values: + if len(values) == 1: + return ValueError(_('only {0} is allowed' + '').format(values[0])) + else: + return ValueError(_('only {0} are allowed' + '').format(display_list(values))) diff --git a/tiramisu/option/dateoption.py b/tiramisu/option/dateoption.py new file mode 100644 index 0000000..55c9304 --- /dev/null +++ b/tiramisu/option/dateoption.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re +from datetime import datetime + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class DateOption(Option): + __slots__ = tuple() + _display_name = _('date') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err + try: + datetime.strptime(value, "%Y-%m-%d") + except ValueError: + return ValueError() diff --git a/tiramisu/option/domainnameoption.py b/tiramisu/option/domainnameoption.py new file mode 100644 index 0000000..765f5cb --- /dev/null +++ b/tiramisu/option/domainnameoption.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re +from IPy import IP + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class DomainnameOption(Option): + """represents the choice of a domain name + netbios: for MS domain + hostname: to identify the device + domainname: + fqdn: with tld, not supported yet + """ + __slots__ = tuple() + _display_name = _('domain name') + + def __init__(self, name, doc, default=None, default_multi=None, + requires=None, multi=False, callback=None, + callback_params=None, validator=None, validator_params=None, + properties=None, allow_ip=False, type_='domainname', + warnings_only=False, allow_without_dot=False): + if type_ not in ['netbios', 'hostname', 'domainname']: + raise ValueError(_('unknown type_ {0} for hostname').format(type_)) + extra = {'_dom_type': type_} + if allow_ip not in [True, False]: + raise ValueError(_('allow_ip must be a boolean')) + if allow_without_dot not in [True, False]: + raise ValueError(_('allow_without_dot must be a boolean')) + extra['_allow_ip'] = allow_ip + extra['_allow_without_dot'] = allow_without_dot + # FIXME should be + # regexp = r'^((?!-)[a-z0-9-]{1,63}(? part_name_length: + return ValueError(_("invalid length (max {0})" + "").format(part_name_length)) + + if self._get_extra('_allow_ip') is True: + try: + IP('{0}/32'.format(value)) + return + except ValueError: + pass + else: + try: + IP('{0}/32'.format(value)) + except ValueError: + pass + else: + return ValueError(_('must not be an IP')) + part_name_length = self._get_len(self._get_extra('_dom_type')) + if self._get_extra('_dom_type') == 'domainname': + if not self._get_extra('_allow_without_dot') and not "." in value: + return ValueError(_("must have dot")) + if len(value) > 255: + return ValueError(_("invalid length (max 255)")) + for dom in value.split('.'): + err = _valid_length(dom) + if err: + return err + else: + return _valid_length(value) + + def _second_level_validation(self, value, warnings_only): + if self._get_extra('_has_upper').search(value): + return ValueError(_('some characters are uppercase')) + if not self._get_extra('_domain_re').search(value): + if warnings_only: + return ValueError(_('some characters may cause problems')) + else: + return ValueError() diff --git a/tiramisu/option/emailoption.py b/tiramisu/option/emailoption.py new file mode 100644 index 0000000..6070e0a --- /dev/null +++ b/tiramisu/option/emailoption.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re + +from ..i18n import _ +from .option import _RegexpOption + + +class EmailOption(_RegexpOption): + __slots__ = tuple() + #https://www.w3.org/TR/html-markup/input.email.html#input.email.attrs.value.single. + _regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$") + _display_name = _('email address') diff --git a/tiramisu/option/filenameoption.py b/tiramisu/option/filenameoption.py new file mode 100644 index 0000000..e849033 --- /dev/null +++ b/tiramisu/option/filenameoption.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re + +from ..i18n import _ +from .option import _RegexpOption + + +class FilenameOption(_RegexpOption): + __slots__ = tuple() + _regexp = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$") + _display_name = _('file name') diff --git a/tiramisu/option/floatoption.py b/tiramisu/option/floatoption.py new file mode 100644 index 0000000..2eea4fa --- /dev/null +++ b/tiramisu/option/floatoption.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class FloatOption(Option): + "represents a choice of a floating point number" + __slots__ = tuple() + _display_name = _('float') + + def _validate(self, value, context=undefined, current_opt=undefined): + if not isinstance(value, float): + return ValueError() diff --git a/tiramisu/option/intoption.py b/tiramisu/option/intoption.py new file mode 100644 index 0000000..9ffcc51 --- /dev/null +++ b/tiramisu/option/intoption.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class IntOption(Option): + "represents a choice of an integer" + __slots__ = tuple() + _display_name = _('integer') + + def _validate(self, value, context=undefined, current_opt=undefined): + if not isinstance(value, int): + return ValueError() diff --git a/tiramisu/option/ipoption.py b/tiramisu/option/ipoption.py new file mode 100644 index 0000000..346eaec --- /dev/null +++ b/tiramisu/option/ipoption.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +from IPy import IP + +from ..error import ConfigError +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class IPOption(Option): + "represents the choice of an ip" + __slots__ = tuple() + _display_name = _('IP') + + def __init__(self, name, doc, default=None, default_multi=None, + requires=None, multi=False, callback=None, + callback_params=None, validator=None, validator_params=None, + properties=None, private_only=False, allow_reserved=False, + warnings_only=False): + extra = {'_private_only': private_only, + '_allow_reserved': allow_reserved} + super(IPOption, self).__init__(name, doc, default=default, + default_multi=default_multi, + callback=callback, + callback_params=callback_params, + requires=requires, + multi=multi, + validator=validator, + validator_params=validator_params, + properties=properties, + warnings_only=warnings_only, + extra=extra) + + def _validate(self, value, context=undefined, current_opt=undefined): + # sometimes an ip term starts with a zero + # but this does not fit in some case, for example bind does not like it + err = self._impl_valid_unicode(value) + if err: + return err + if value.count('.') != 3: + return ValueError() + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + return ValueError() + # 'standard' validation + try: + IP('{0}/32'.format(value)) + except ValueError: + return ValueError() + + def _second_level_validation(self, value, warnings_only): + ip = IP('{0}/32'.format(value)) + if not self._get_extra('_allow_reserved') and ip.iptype() == 'RESERVED': + if warnings_only: + msg = _("shouldn't in reserved class") + else: + msg = _("mustn't be in reserved class") + return ValueError(msg) + if self._get_extra('_private_only') and ip.iptype() != 'PRIVATE': + if warnings_only: + msg = _("should be in private class") + else: + msg = _("must be in private class") + return ValueError(msg) + + def _cons_in_network(self, current_opt, opts, vals, warnings_only): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + if None in vals: + return + ip, network, netmask = vals + if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): + msg = _('{4} is not in network {0}/{1} ({2}/{3})') + return ValueError(msg.format(network, netmask, + opts[1].impl_getname(), opts[2].impl_getname(), ip)) + # test if ip is not network/broadcast IP + return opts[2]._cons_ip_netmask(current_opt, (opts[2], opts[0]), (netmask, ip), warnings_only) diff --git a/tiramisu/option/netmaskoption.py b/tiramisu/option/netmaskoption.py new file mode 100644 index 0000000..daa65f6 --- /dev/null +++ b/tiramisu/option/netmaskoption.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +from IPy import IP + +from ..error import ConfigError +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class NetmaskOption(Option): + "represents the choice of a netmask" + __slots__ = tuple() + _display_name = _('netmask address') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err + if value.count('.') != 3: + return ValueError() + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + return ValueError() + try: + IP('0.0.0.0/{0}'.format(value)) + except ValueError: + return ValueError() + + def _cons_network_netmask(self, current_opt, opts, vals, warnings_only): + #opts must be (netmask, network) options + if None in vals: + return + return self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only) + + def _cons_ip_netmask(self, current_opt, opts, vals, warnings_only): + #opts must be (netmask, ip) options + if None in vals: + return + return self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only) + + def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net, + warnings_only): + if len(opts) != 2: + return ConfigError(_('invalid len for opts')) + msg = None + try: + ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), + make_net=make_net) + #if cidr == 32, ip same has network + if make_net and ip.prefixlen() != 32: + val_ip = IP(val_ipnetwork) + if ip.net() == val_ip: + msg = _("this is a network with netmask {0} ({1})") + if ip.broadcast() == val_ip: + msg = _("this is a broadcast with netmask {0} ({1})") + + except ValueError: + if not make_net: + msg = _('with netmask {0} ({1})') + if msg is not None: + return ValueError(msg.format(val_netmask, opts[1].impl_getname())) diff --git a/tiramisu/option/networkoption.py b/tiramisu/option/networkoption.py new file mode 100644 index 0000000..f891c4c --- /dev/null +++ b/tiramisu/option/networkoption.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +from IPy import IP + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class NetworkOption(Option): + "represents the choice of a network" + __slots__ = tuple() + _display_name = _('network address') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err + if value.count('.') != 3: + return ValueError() + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + return ValueError() + try: + IP(value) + except ValueError: + return ValueError() + + def _second_level_validation(self, value, warnings_only): + ip = IP(value) + if ip.iptype() == 'RESERVED': + if warnings_only: + msg = _("shouldn't be in reserved class") + else: + msg = _("mustn't be in reserved class") + return ValueError(msg) diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 5843ff3..9545e49 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -19,580 +19,14 @@ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the whole pypy projet is under MIT licence # ____________________________________________________________ -import re -import sys -import datetime -from IPy import IP -from types import FunctionType from ..setting import undefined -from ..error import ConfigError -from ..i18n import _ -from .baseoption import Option, validate_callback, display_list -from ..autolib import carry_out_calculation - - -class ChoiceOption(Option): - """represents a choice out of several objects. - - The option can also have the value ``None`` - """ - __slots__ = tuple() - _display_name = _('choice') - - def __init__(self, name, doc, values, default=None, - values_params=None, default_multi=None, requires=None, - multi=False, callback=None, callback_params=None, - validator=None, validator_params=None, - properties=None, warnings_only=False): - """ - :param values: is a list of values the option can possibly take - """ - if isinstance(values, FunctionType): - validate_callback(values, values_params, 'values', self) - else: - if values_params is not None: - raise ValueError(_('values is not a function, so values_params must be None')) - if not isinstance(values, tuple): - raise TypeError(_('values must be a tuple or a function for {0}' - ).format(name)) - self._choice_values = values - if values_params is not None: - self._choice_values_params = values_params - super(ChoiceOption, self).__init__(name, doc, default=default, - default_multi=default_multi, - callback=callback, - callback_params=callback_params, - requires=requires, - multi=multi, - validator=validator, - validator_params=validator_params, - properties=properties, - warnings_only=warnings_only) - - def impl_get_values(self, context, current_opt=undefined): - if current_opt is undefined: - current_opt = self - #FIXME cache? but in context... - values = self._choice_values - if isinstance(values, FunctionType): - if context is None: - values = [] - else: - values = carry_out_calculation(current_opt, context=context, - callback=values, - callback_params=getattr(self, '_choice_values_params', {})) - if isinstance(values, Exception): - return values - if values is not undefined and not isinstance(values, list): - raise ConfigError(_('calculated values for {0} is not a list' - '').format(self.impl_getname())) - return values - - - def _validate(self, value, context=undefined, current_opt=undefined): - values = self.impl_get_values(context, current_opt=current_opt) - if isinstance(values, Exception): - return values - if values is not undefined and not value in values: - if len(values) == 1: - return ValueError(_('only {0} is allowed' - '').format(values[0])) - else: - return ValueError(_('only {0} are allowed' - '').format(display_list(values))) - - -class BoolOption(Option): - "represents a choice between ``True`` and ``False``" - __slots__ = tuple() - _display_name = _('boolean') - - def _validate(self, value, context=undefined, current_opt=undefined): - if not isinstance(value, bool): - return ValueError() - - -class IntOption(Option): - "represents a choice of an integer" - __slots__ = tuple() - _display_name = _('integer') - - def _validate(self, value, context=undefined, current_opt=undefined): - if not isinstance(value, int): - return ValueError() - - -class FloatOption(Option): - "represents a choice of a floating point number" - __slots__ = tuple() - _display_name = _('float') - - def _validate(self, value, context=undefined, current_opt=undefined): - if not isinstance(value, float): - return ValueError() - - -class StrOption(Option): - "represents the choice of a string" - __slots__ = tuple() - _display_name = _('string') - - def _validate(self, value, context=undefined, current_opt=undefined): - if not isinstance(value, str): - return ValueError() - - -if sys.version_info[0] >= 3: # pragma: no cover - #UnicodeOption is same as StrOption in python 3+ - class UnicodeOption(StrOption): - __slots__ = tuple() - pass -else: - class UnicodeOption(Option): - "represents the choice of a unicode string" - __slots__ = tuple() - _empty = u'' - _display_name = _('unicode string') - - def _validate(self, value, context=undefined, current_opt=undefined): - if not isinstance(value, unicode): - return ValueError() - - -class PasswordOption(Option): - "represents the choice of a password" - __slots__ = tuple() - _display_name = _('password') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - - -class IPOption(Option): - "represents the choice of an ip" - __slots__ = tuple() - _display_name = _('IP') - - def __init__(self, name, doc, default=None, default_multi=None, - requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_params=None, - properties=None, private_only=False, allow_reserved=False, - warnings_only=False): - extra = {'_private_only': private_only, - '_allow_reserved': allow_reserved} - super(IPOption, self).__init__(name, doc, default=default, - default_multi=default_multi, - callback=callback, - callback_params=callback_params, - requires=requires, - multi=multi, - validator=validator, - validator_params=validator_params, - properties=properties, - warnings_only=warnings_only, - extra=extra) - - def _validate(self, value, context=undefined, current_opt=undefined): - # sometimes an ip term starts with a zero - # but this does not fit in some case, for example bind does not like it - err = self._impl_valid_unicode(value) - if err: - return err - if value.count('.') != 3: - return ValueError() - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - return ValueError() - # 'standard' validation - try: - IP('{0}/32'.format(value)) - except ValueError: - return ValueError() - - def _second_level_validation(self, value, warnings_only): - ip = IP('{0}/32'.format(value)) - if not self._get_extra('_allow_reserved') and ip.iptype() == 'RESERVED': - if warnings_only: - msg = _("shouldn't in reserved class") - else: - msg = _("mustn't be in reserved class") - return ValueError(msg) - if self._get_extra('_private_only') and not ip.iptype() == 'PRIVATE': - if warnings_only: - msg = _("should be in private class") - else: - msg = _("must be in private class") - return ValueError(msg) - - def _cons_in_network(self, current_opt, opts, vals, warnings_only): - if len(vals) != 3: - raise ConfigError(_('invalid len for vals')) - if None in vals: - return - ip, network, netmask = vals - if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): - msg = _('{4} is not in network {0}/{1} ({2}/{3})') - return ValueError(msg.format(network, netmask, - opts[1].impl_getname(), opts[2].impl_getname(), ip)) - # test if ip is not network/broadcast IP - return opts[2]._cons_ip_netmask(current_opt, (opts[2], opts[0]), (netmask, ip), warnings_only) - - -class PortOption(Option): - """represents the choice of a port - The port numbers are divided into three ranges: - the well-known ports, - the registered ports, - and the dynamic or private ports. - You can actived this three range. - Port number 0 is reserved and can't be used. - see: http://en.wikipedia.org/wiki/Port_numbers - """ - __slots__ = tuple() - port_re = re.compile(r"^[0-9]*$") - _display_name = _('port') - - def __init__(self, name, doc, default=None, default_multi=None, - requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_params=None, - properties=None, allow_range=False, allow_zero=False, - allow_wellknown=True, allow_registred=True, - allow_private=False, warnings_only=False): - extra = {'_allow_range': allow_range, - '_min_value': None, - '_max_value': None} - ports_min = [0, 1, 1024, 49152] - ports_max = [0, 1023, 49151, 65535] - is_finally = False - for index, allowed in enumerate([allow_zero, - allow_wellknown, - allow_registred, - allow_private]): - if extra['_min_value'] is None: - if allowed: - extra['_min_value'] = ports_min[index] - elif not allowed: - is_finally = True - elif allowed and is_finally: - raise ValueError(_('inconsistency in allowed range')) - if allowed: - extra['_max_value'] = ports_max[index] - - if extra['_max_value'] is None: - raise ValueError(_('max value is empty')) - - super(PortOption, self).__init__(name, doc, default=default, - default_multi=default_multi, - callback=callback, - callback_params=callback_params, - requires=requires, - multi=multi, - validator=validator, - validator_params=validator_params, - properties=properties, - warnings_only=warnings_only, - extra=extra) - - def _validate(self, value, context=undefined, current_opt=undefined): - if isinstance(value, int): - if sys.version_info[0] >= 3: # pragma: no cover - value = str(value) - else: - value = unicode(value) - err = self._impl_valid_unicode(value) - if err: - return err - if self._get_extra('_allow_range') and ":" in str(value): - value = str(value).split(':') - if len(value) != 2: - return ValueError(_('range must have two values only')) - if not value[0] < value[1]: - return ValueError(_('first port in range must be' - ' smaller than the second one')) - else: - value = [value] - - for val in value: - if not self.port_re.search(val): - return ValueError() - val = int(val) - if not self._get_extra('_min_value') <= val <= self._get_extra('_max_value'): - return ValueError(_('must be an integer between {0} ' - 'and {1}').format(self._get_extra('_min_value'), - self._get_extra('_max_value'))) - - -class NetworkOption(Option): - "represents the choice of a network" - __slots__ = tuple() - _display_name = _('network address') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - if value.count('.') != 3: - return ValueError() - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - return ValueError() - try: - IP(value) - except ValueError: - return ValueError() - - def _second_level_validation(self, value, warnings_only): - ip = IP(value) - if ip.iptype() == 'RESERVED': - if warnings_only: - msg = _("shouldn't be in reserved class") - else: - msg = _("mustn't be in reserved class") - return ValueError(msg) - - -class NetmaskOption(Option): - "represents the choice of a netmask" - __slots__ = tuple() - _display_name = _('netmask address') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - if value.count('.') != 3: - return ValueError() - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - return ValueError() - try: - IP('0.0.0.0/{0}'.format(value)) - except ValueError: - return ValueError() - - def _cons_network_netmask(self, current_opt, opts, vals, warnings_only): - #opts must be (netmask, network) options - if None in vals: - return - return self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only) - - def _cons_ip_netmask(self, current_opt, opts, vals, warnings_only): - #opts must be (netmask, ip) options - if None in vals: - return - return self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only) - - def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net, - warnings_only): - if len(opts) != 2: - return ConfigError(_('invalid len for opts')) - msg = None - try: - ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), - make_net=make_net) - #if cidr == 32, ip same has network - if make_net and ip.prefixlen() != 32: - val_ip = IP(val_ipnetwork) - if ip.net() == val_ip: - msg = _("this is a network with netmask {0} ({1})") - if ip.broadcast() == val_ip: - msg = _("this is a broadcast with netmask {0} ({1})") - - except ValueError: - if not make_net: - msg = _('with netmask {0} ({1})') - if msg is not None: - return ValueError(msg.format(val_netmask, opts[1].impl_getname())) - - -class BroadcastOption(Option): - __slots__ = tuple() - _display_name = _('broadcast address') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - if value.count('.') != 3: - return ValueError() - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - return ValueError() - try: - IP('{0}/32'.format(value)) - except ValueError: - return ValueError() - - def _cons_broadcast(self, current_opt, opts, vals, warnings_only): - if len(vals) != 3: - raise ConfigError(_('invalid len for vals')) - if None in vals: - return - broadcast, network, netmask = vals - if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): - return ValueError(_('broadcast {4} invalid with network {0}/{1} ({2}/{3})').format( - network, netmask, opts[1].impl_getname(), opts[2].impl_getname(), broadcast)) - - -class DomainnameOption(Option): - """represents the choice of a domain name - netbios: for MS domain - hostname: to identify the device - domainname: - fqdn: with tld, not supported yet - """ - __slots__ = tuple() - _display_name = _('domain name') - - def __init__(self, name, doc, default=None, default_multi=None, - requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_params=None, - properties=None, allow_ip=False, type_='domainname', - warnings_only=False, allow_without_dot=False): - if type_ not in ['netbios', 'hostname', 'domainname']: - raise ValueError(_('unknown type_ {0} for hostname').format(type_)) - extra = {'_dom_type': type_} - if allow_ip not in [True, False]: - raise ValueError(_('allow_ip must be a boolean')) - if allow_without_dot not in [True, False]: - raise ValueError(_('allow_without_dot must be a boolean')) - extra['_allow_ip'] = allow_ip - extra['_allow_without_dot'] = allow_without_dot - # FIXME should be - # regexp = r'^((?!-)[a-z0-9-]{1,63}(? part_name_length: - return ValueError(_("invalid length (max {0})" - "").format(part_name_length)) - - if self._get_extra('_allow_ip') is True: - try: - IP('{0}/32'.format(value)) - return - except ValueError: - pass - else: - try: - IP('{0}/32'.format(value)) - except ValueError: - pass - else: - return ValueError(_('must not be an IP')) - part_name_length = self._get_len(self._get_extra('_dom_type')) - if self._get_extra('_dom_type') == 'domainname': - if not self._get_extra('_allow_without_dot') and not "." in value: - return ValueError(_("must have dot")) - if len(value) > 255: - return ValueError(_("invalid length (max 255)")) - for dom in value.split('.'): - err = _valid_length(dom) - if err: - return err - else: - return _valid_length(value) - - def _second_level_validation(self, value, warnings_only): - if self._get_extra('_has_upper').search(value): - return ValueError(_('some characters are uppercase')) - if not self._get_extra('_domain_re').search(value): - if warnings_only: - return ValueError(_('some characters may cause problems')) - else: - return ValueError() - - -class URLOption(DomainnameOption): - __slots__ = tuple() - proto_re = re.compile(r'(http|https)://') - path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") - _display_name = _('URL') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - match = self.proto_re.search(value) - if not match: - return ValueError(_('must start with http:// or ' - 'https://')) - value = value[len(match.group(0)):] - # get domain/files - splitted = value.split('/', 1) - if len(splitted) == 1: - domain = value - files = None - else: - domain, files = splitted - # if port in domain - splitted = domain.split(':', 1) - if len(splitted) == 1: - domain = splitted[0] - port = 0 - else: - domain, port = splitted - if not 0 <= int(port) <= 65535: - return ValueError(_('port must be an between 0 and ' - '65536')) - # validate domainname - err = super(URLOption, self)._validate(domain) - if err: - return err - err = super(URLOption, self)._second_level_validation(domain, False) - if err: - return err - # validate file - if files is not None and files != '' and not self.path_re.search(files): - return ValueError(_('must ends with a valid resource name')) - - def _second_level_validation(self, value, warnings_only): - pass +from .baseoption import Option class _RegexpOption(Option): __slots__ = tuple() + def _validate(self, value, context=undefined, current_opt=undefined): err = self._impl_valid_unicode(value) if err: @@ -600,37 +34,3 @@ class _RegexpOption(Option): match = self._regexp.search(value) if not match: return ValueError() - - -class EmailOption(_RegexpOption): - __slots__ = tuple() - #https://www.w3.org/TR/html-markup/input.email.html#input.email.attrs.value.single. - _regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$") - _display_name = _('email address') - - -class UsernameOption(_RegexpOption): - __slots__ = tuple() - #regexp build with 'man 8 adduser' informations - _regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") - _display_name = _('username') - - -class FilenameOption(_RegexpOption): - __slots__ = tuple() - _regexp = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$") - _display_name = _('file name') - - -class DateOption(Option): - __slots__ = tuple() - _display_name = _('date') - - def _validate(self, value, context=undefined, current_opt=undefined): - err = self._impl_valid_unicode(value) - if err: - return err - try: - datetime.datetime.strptime(value,"%Y-%m-%d") - except ValueError: - return ValueError() diff --git a/tiramisu/option/passwordoption.py b/tiramisu/option/passwordoption.py new file mode 100644 index 0000000..57e5a2d --- /dev/null +++ b/tiramisu/option/passwordoption.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class PasswordOption(Option): + "represents the choice of a password" + __slots__ = tuple() + _display_name = _('password') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err diff --git a/tiramisu/option/portoption.py b/tiramisu/option/portoption.py new file mode 100644 index 0000000..db851b0 --- /dev/null +++ b/tiramisu/option/portoption.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re +import sys + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class PortOption(Option): + """represents the choice of a port + The port numbers are divided into three ranges: + the well-known ports, + the registered ports, + and the dynamic or private ports. + You can actived this three range. + Port number 0 is reserved and can't be used. + see: http://en.wikipedia.org/wiki/Port_numbers + """ + __slots__ = tuple() + port_re = re.compile(r"^[0-9]*$") + _display_name = _('port') + + def __init__(self, name, doc, default=None, default_multi=None, + requires=None, multi=False, callback=None, + callback_params=None, validator=None, validator_params=None, + properties=None, allow_range=False, allow_zero=False, + allow_wellknown=True, allow_registred=True, + allow_private=False, warnings_only=False): + extra = {'_allow_range': allow_range, + '_min_value': None, + '_max_value': None} + ports_min = [0, 1, 1024, 49152] + ports_max = [0, 1023, 49151, 65535] + is_finally = False + for index, allowed in enumerate([allow_zero, + allow_wellknown, + allow_registred, + allow_private]): + if extra['_min_value'] is None: + if allowed: + extra['_min_value'] = ports_min[index] + elif not allowed: + is_finally = True + elif allowed and is_finally: + raise ValueError(_('inconsistency in allowed range')) + if allowed: + extra['_max_value'] = ports_max[index] + + if extra['_max_value'] is None: + raise ValueError(_('max value is empty')) + + super(PortOption, self).__init__(name, doc, default=default, + default_multi=default_multi, + callback=callback, + callback_params=callback_params, + requires=requires, + multi=multi, + validator=validator, + validator_params=validator_params, + properties=properties, + warnings_only=warnings_only, + extra=extra) + + def _validate(self, value, context=undefined, current_opt=undefined): + if isinstance(value, int): + if sys.version_info[0] >= 3: # pragma: no cover + value = str(value) + else: + value = unicode(value) + err = self._impl_valid_unicode(value) + if err: + return err + if self._get_extra('_allow_range') and ":" in str(value): + value = str(value).split(':') + if len(value) != 2: + return ValueError(_('range must have two values only')) + if not value[0] < value[1]: + return ValueError(_('first port in range must be' + ' smaller than the second one')) + else: + value = [value] + + for val in value: + if not self.port_re.search(val): + return ValueError() + val = int(val) + if not self._get_extra('_min_value') <= val <= self._get_extra('_max_value'): + return ValueError(_('must be an integer between {0} ' + 'and {1}').format(self._get_extra('_min_value'), + self._get_extra('_max_value'))) diff --git a/tiramisu/option/stroption.py b/tiramisu/option/stroption.py new file mode 100644 index 0000000..9a8c884 --- /dev/null +++ b/tiramisu/option/stroption.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import sys + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option + + +class StrOption(Option): + "represents the choice of a string" + __slots__ = tuple() + _display_name = _('string') + + def _validate(self, value, context=undefined, current_opt=undefined): + if not isinstance(value, str): + return ValueError() + + +if sys.version_info[0] >= 3: # pragma: no cover + #UnicodeOption is same as StrOption in python 3+ + class UnicodeOption(StrOption): + __slots__ = tuple() + pass +else: + class UnicodeOption(Option): + "represents the choice of a unicode string" + __slots__ = tuple() + _empty = u'' + _display_name = _('unicode string') + + def _validate(self, value, context=undefined, current_opt=undefined): + if not isinstance(value, unicode): + return ValueError() diff --git a/tiramisu/option/urloption.py b/tiramisu/option/urloption.py new file mode 100644 index 0000000..5574641 --- /dev/null +++ b/tiramisu/option/urloption.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re + +from ..setting import undefined +from ..i18n import _ +from .baseoption import Option +from .domainnameoption import DomainnameOption + + +class URLOption(DomainnameOption): + __slots__ = tuple() + proto_re = re.compile(r'(http|https)://') + path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") + _display_name = _('URL') + + def _validate(self, value, context=undefined, current_opt=undefined): + err = self._impl_valid_unicode(value) + if err: + return err + match = self.proto_re.search(value) + if not match: + return ValueError(_('must start with http:// or ' + 'https://')) + value = value[len(match.group(0)):] + # get domain/files + splitted = value.split('/', 1) + if len(splitted) == 1: + domain = value + files = None + else: + domain, files = splitted + # if port in domain + splitted = domain.split(':', 1) + if len(splitted) == 1: + domain = splitted[0] + port = 0 + else: + domain, port = splitted + if not 0 <= int(port) <= 65535: + return ValueError(_('port must be an between 0 and ' + '65536')) + # validate domainname + err = super(URLOption, self)._validate(domain) + if err: + return err + err = super(URLOption, self)._second_level_validation(domain, False) + if err: + return err + # validate file + if files is not None and files != '' and not self.path_re.search(files): + return ValueError(_('must ends with a valid resource name')) + + def _second_level_validation(self, value, warnings_only): + pass diff --git a/tiramisu/option/usernameoption.py b/tiramisu/option/usernameoption.py new file mode 100644 index 0000000..bbf860f --- /dev/null +++ b/tiramisu/option/usernameoption.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2017 Team tiramisu (see AUTHORS for all contributors) +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# The original `Config` design model is unproudly borrowed from +# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ +# the whole pypy projet is under MIT licence +# ____________________________________________________________ +import re + +from ..i18n import _ +from .option import _RegexpOption + + +class UsernameOption(_RegexpOption): + __slots__ = tuple() + #regexp build with 'man 8 adduser' informations + _regexp = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") + _display_name = _('username')