_validation, _second_level_validation => validation, second_level_validation

This commit is contained in:
Emmanuel Garette 2019-11-19 08:26:42 +01:00
parent 5c3a133928
commit 74e604478e
15 changed files with 316 additions and 269 deletions

View File

@ -30,9 +30,7 @@ class BoolOption(Option):
_type = 'boolean' _type = 'boolean'
_display_name = _('boolean') _display_name = _('boolean')
def _validate(self, def validate(self,
value: bool, value: bool) -> None:
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, bool): if not isinstance(value, bool):
raise ValueError() raise ValueError()

View File

@ -31,10 +31,8 @@ class BroadcastOption(Option):
_type = 'broadcast_address' _type = 'broadcast_address'
_display_name = _('broadcast address') _display_name = _('broadcast address')
def _validate(self, def validate(self,
value: str, value: str) -> None:
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_('invalid string')) raise ValueError(_('invalid string'))
if value.count('.') != 3: if value.count('.') != 3:

View File

@ -18,12 +18,12 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from types import FunctionType from typing import Any
from ..setting import undefined from ..setting import undefined, OptionBag
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option
from ..autolib import carry_out_calculation, Calculation from ..autolib import Calculation
from ..error import ConfigError, display_list from ..error import ConfigError, display_list
@ -40,12 +40,8 @@ class ChoiceOption(Option):
name, name,
doc, doc,
values, values,
default=None, *args,
default_multi=None, **kwargs):
multi=False,
validators=None,
properties=None,
warnings_only=False):
""" """
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
@ -56,12 +52,8 @@ class ChoiceOption(Option):
self._choice_values = values self._choice_values = values
super(ChoiceOption, self).__init__(name, super(ChoiceOption, self).__init__(name,
doc, doc,
default=default, *args,
default_multi=default_multi, **kwargs)
multi=multi,
validators=validators,
properties=properties,
warnings_only=warnings_only)
def impl_get_values(self, def impl_get_values(self,
option_bag): option_bag):
@ -74,15 +66,17 @@ class ChoiceOption(Option):
values = self._choice_values values = self._choice_values
return values return values
def _validate(self, def validate(self,
value, value: Any) -> None:
option_bag, pass
current_opt=undefined):
def validate_with_option(self,
value: Any,
option_bag: OptionBag) -> None:
values = self.impl_get_values(option_bag) values = self.impl_get_values(option_bag)
if values is not undefined and value not in values: if values is not undefined and value not in values:
if len(values) == 1: if len(values) == 1:
raise ValueError(_('only "{0}" is allowed' raise ValueError(_('only "{0}" is allowed'
'').format(values[0])) '').format(values[0]))
else: raise ValueError(_('only {0} are allowed'
raise ValueError(_('only {0} are allowed' '').format(display_list(values, add_quote=True)))
'').format(display_list(values, add_quote=True)))

View File

@ -22,20 +22,17 @@ from datetime import datetime
from ..setting import undefined, Undefined, OptionBag from ..setting import undefined, Undefined, OptionBag
from ..i18n import _ from ..i18n import _
from .option import Option from .stroption import StrOption
class DateOption(Option): class DateOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = 'date' _type = 'date'
_display_name = _('date') _display_name = _('date')
def _validate(self, def validate(self,
value: str, value: str) -> None:
option_bag: OptionBag, super().validate(value)
current_opt: Option=Undefined) -> None:
if not isinstance(value, str):
raise ValueError(_('invalid string'))
try: try:
datetime.strptime(value, "%Y-%m-%d") datetime.strptime(value, "%Y-%m-%d")
except ValueError: except ValueError:

View File

@ -19,14 +19,18 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
import re import re
from ipaddress import ip_address from ipaddress import ip_interface
from ..setting import undefined, Undefined, OptionBag from typing import Any, Optional, List
from ..i18n import _ from ..i18n import _
from .option import Option from ..setting import undefined
from .ipoption import IPOption from .ipoption import IPOption
from .stroption import StrOption
from .networkoption import NetworkOption
from .option import Calculation
class DomainnameOption(IPOption): class DomainnameOption(StrOption):
"""represents the choice of a domain name """represents the choice of a domain name
netbios: for MS domain netbios: for MS domain
hostname: to identify the device hostname: to identify the device
@ -38,53 +42,63 @@ class DomainnameOption(IPOption):
_display_name = _('domain name') _display_name = _('domain name')
def __init__(self, def __init__(self,
name, name: str,
doc, doc: str,
default=None, default: Any=undefined,
default_multi=None, default_multi: Any=None,
multi: bool=False, multi: bool=False,
validators=None, validators: Optional[List[Calculation]]=None,
properties=None, properties: Optional[List[str]]=None,
allow_ip: bool=False,
cidr: bool=False,
type_: str='domainname',
warnings_only: bool=False, warnings_only: bool=False,
allow_without_dot=False) -> None: allow_ip: bool=False,
allow_network: bool=False,
network_cidr: bool=False,
type: str='domainname',
allow_without_dot: bool=False,
allow_startswith_dot: bool=False) -> None:
if type_ not in ['netbios', 'hostname', 'domainname']: if type not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_)) raise ValueError(_('unknown type {0} for hostname').format(type))
extra = {'_dom_type': type_} extra = {'_dom_type': type}
if allow_ip not in [True, False]: if not isinstance(allow_ip, bool):
raise ValueError(_('allow_ip must be a boolean')) raise ValueError(_('allow_ip must be a boolean'))
if allow_without_dot not in [True, False]: if not isinstance(allow_network, bool):
raise ValueError(_('allow_network must be a boolean'))
if not isinstance(allow_without_dot, bool):
raise ValueError(_('allow_without_dot must be a boolean')) raise ValueError(_('allow_without_dot must be a boolean'))
extra['_allow_ip'] = allow_ip if not isinstance(allow_startswith_dot, bool):
raise ValueError(_('allow_startswith_dot must be a boolean'))
extra['_allow_without_dot'] = allow_without_dot extra['_allow_without_dot'] = allow_without_dot
if type_ == 'domainname': if type == 'domainname':
if allow_without_dot: if allow_without_dot:
min_time = 0 min_time = 0
else: else:
min_time = 1 min_time = 1
regexp = r'((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}'.format(self._get_len(type_), min_time) regexp = r'((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}'.format(self._get_len(type), min_time)
msg = _('only lowercase, number, "-" and "." are characters are allowed') msg = _('only lowercase, number, "-" and "." characters are allowed')
msg_warning = _('only lowercase, number, "-" and "." are characters are recommanded') msg_warning = _('only lowercase, number, "-" and "." characters are recommanded')
else: else:
regexp = r'((?!-)[a-z0-9-]{{1,{0}}})'.format(self._get_len(type_)) regexp = r'((?!-)[a-z0-9-]{{1,{0}}})'.format(self._get_len(type))
msg = _('only lowercase, number and - are characters are allowed') msg = _('only lowercase, number and "-" characters are allowed')
msg_warning = _('only lowercase, number and "-" are characters are recommanded') msg_warning = _('only lowercase, number and "-" characters are recommanded')
if allow_ip: if allow_ip:
msg = _('could be a IP, otherwise {}').format(msg) msg = _('could be a IP, otherwise {}').format(msg)
msg_warning = _('could be a IP, otherwise {}').format(msg_warning) msg_warning = _('could be a IP, otherwise {}').format(msg_warning)
if not cidr: regexp = r'^{0}$'.format(regexp)
regexp = r'^(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))$'.format(regexp)
else:
regexp = r'^(?:{0}|(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){{3}}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[0-9][0-9]))$'.format(regexp)
else:
regexp = r'^{0}$'.format(regexp)
extra['_domain_re'] = re.compile(regexp) extra['_domain_re'] = re.compile(regexp)
extra['_domain_re_message'] = msg extra['_domain_re_message'] = msg
extra['_domain_re_message_warning'] = msg_warning extra['_domain_re_message_warning'] = msg_warning
extra['_has_upper'] = re.compile('[A-Z]') extra['_has_upper'] = re.compile('[A-Z]')
if allow_ip:
extra['_ip'] = IPOption(name,
doc)
extra['_allow_ip'] = allow_ip
if allow_network:
extra['_network'] = NetworkOption(name,
doc,
cidr=network_cidr)
extra['_allow_network'] = allow_network
extra['_allow_startswith_dot'] = allow_startswith_dot
super().__init__(name, super().__init__(name,
doc, doc,
@ -94,19 +108,16 @@ class DomainnameOption(IPOption):
validators=validators, validators=validators,
properties=properties, properties=properties,
warnings_only=warnings_only, warnings_only=warnings_only,
cidr=cidr, extra=extra)
_extra=extra)
def _get_len(self, type_): def _get_len(self, type):
if type_ == 'netbios': if type == 'netbios':
return 15 return 15
else: else:
return 63 return 63
def _validate(self, def _validate_domain(self,
value: str, value: str) -> None:
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
def _valid_length(val): def _valid_length(val):
if len(val) < 1: if len(val) < 1:
raise ValueError(_("invalid length (min 1)")) raise ValueError(_("invalid length (min 1)"))
@ -114,33 +125,83 @@ class DomainnameOption(IPOption):
raise ValueError(_("invalid length (max {0})" raise ValueError(_("invalid length (max {0})"
"").format(part_name_length)) "").format(part_name_length))
if not isinstance(value, str):
raise ValueError(_('invalid string'))
try:
ip_address(value)
except ValueError:
pass
else:
if self.impl_get_extra('_allow_ip') is False:
raise ValueError(_('must not be an IP'))
# it's an IP so validate with IPOption
return super()._validate(value, option_bag, current_opt)
part_name_length = self._get_len(self.impl_get_extra('_dom_type')) part_name_length = self._get_len(self.impl_get_extra('_dom_type'))
if self.impl_get_extra('_dom_type') == 'domainname': if self.impl_get_extra('_dom_type') == 'domainname':
if not self.impl_get_extra('_allow_without_dot') and not "." in value: if not self.impl_get_extra('_allow_without_dot') and not "." in value:
raise ValueError(_("must have dot")) raise ValueError(_("must have dot"))
if len(value) > 255: if len(value) > 255:
raise ValueError(_("invalid length (max 255)")) raise ValueError(_("invalid length (max 255)"))
for dom in value.split('.'): if self.impl_get_extra('_allow_startswith_dot') and value.startswith('.'):
val = value[1:]
else:
val = value
for dom in val.split('.'):
_valid_length(dom) _valid_length(dom)
else: else:
_valid_length(value) _valid_length(value)
def _second_level_validation(self, value, warnings_only): def _validate_ip_network(self,
value: str) -> None:
allow_ip = self.impl_get_extra('_allow_ip')
allow_network = self.impl_get_extra('_allow_network')
if allow_ip is False and allow_network is False:
raise ValueError(_('must not be an IP'))
if allow_ip is True:
try:
self.impl_get_extra('_ip').validate(value)
return
except ValueError as err:
if allow_network is False:
raise err
if allow_network is True:
self.impl_get_extra('_network').validate(value)
def validate(self,
value: str) -> None:
super().validate(value)
try:
# check if it's an IP or network
ip_interface(value)
except ValueError:
self._validate_domain(value)
else:
self._validate_ip_network(value)
def _second_level_validation_domain(self,
value: str,
warnings_only: bool) -> None:
if self.impl_get_extra('_has_upper').search(value): if self.impl_get_extra('_has_upper').search(value):
raise ValueError(_('some characters are uppercase')) raise ValueError(_('some characters are uppercase'))
if not self.impl_get_extra('_domain_re').search(value): if not self.impl_get_extra('_domain_re').search(value):
if warnings_only: if warnings_only:
raise ValueError(self.impl_get_extra('_domain_re_message_warning')) raise ValueError(self.impl_get_extra('_domain_re_message_warning'))
else: raise ValueError(self.impl_get_extra('_domain_re_message'))
raise ValueError(self.impl_get_extra('_domain_re_message'))
def _second_level_validation_ip_network(self,
value: str,
warnings_only: bool) -> None:
allow_ip = self.impl_get_extra('_allow_ip')
allow_network = self.impl_get_extra('_allow_network')
# it's an IP so validate with IPOption
if allow_ip is False and allow_network is False:
raise ValueError(_('must not be an IP'))
if allow_ip is True:
try:
self.impl_get_extra('_ip').second_level_validation(value, warnings_only)
return
except ValueError as err:
if allow_network is False:
raise err
if allow_network is True:
self.impl_get_extra('_network').second_level_validation(value, warnings_only)
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
try:
# check if it's an IP or network
ip_interface(value)
except ValueError:
self._second_level_validation_domain(value, warnings_only)
else:
self._second_level_validation_ip_network(value, warnings_only)

View File

@ -26,7 +26,6 @@ from .stroption import RegexpOption
class EmailOption(RegexpOption): class EmailOption(RegexpOption):
__slots__ = tuple() __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-]+)*$") _regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
_type = 'email' _type = 'email'
_display_name = _('email address') _display_name = _('email address')

View File

@ -30,9 +30,7 @@ class FloatOption(Option):
_type = 'float' _type = 'float'
_display_name = _('float') _display_name = _('float')
def _validate(self, def validate(self,
value: float, value: float) -> None:
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, float): if not isinstance(value, float):
raise ValueError() raise ValueError()

View File

@ -31,7 +31,6 @@ class IntOption(Option):
_display_name = _('integer') _display_name = _('integer')
def __init__(self, def __init__(self,
name,
*args, *args,
min_number=None, min_number=None,
max_number=None, max_number=None,
@ -41,18 +40,27 @@ class IntOption(Option):
extra['min_number'] = min_number extra['min_number'] = min_number
if max_number is not None: if max_number is not None:
extra['max_number'] = max_number extra['max_number'] = max_number
super().__init__(name, extra=extra, *args, **kwargs) super().__init__(*args, extra=extra, **kwargs)
def validate(self,
def _validate(self, value: int) -> None:
value: int,
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError() raise ValueError()
def second_level_validation(self,
value,
warnings_only):
min_number = self.impl_get_extra('min_number') min_number = self.impl_get_extra('min_number')
if min_number is not None and value < min_number: if min_number is not None and value < min_number:
raise ValueError(_('value must be greater than "{0}"').format(min_number)) if warnings_only:
msg = 'value should be greater than "{0}"'
else:
msg = 'value must be greater than "{0}"'
raise ValueError(_(msg).format(min_number))
max_number = self.impl_get_extra('max_number') max_number = self.impl_get_extra('max_number')
if max_number is not None and value > max_number: if max_number is not None and value > max_number:
raise ValueError(_('value must be less than "{0}"').format(max_number)) if warnings_only:
msg = 'value should be less than "{0}"'
else:
msg = 'value must be less than "{0}"'
raise ValueError(_(msg).format(max_number))

View File

@ -35,69 +35,48 @@ class IPOption(StrOption):
_display_name = _('IP') _display_name = _('IP')
def __init__(self, def __init__(self,
name, *args,
doc,
default=None,
default_multi=None,
multi=False,
validators=None,
properties=None,
private_only=False, private_only=False,
allow_reserved=False, allow_reserved=False,
warnings_only=False,
cidr=False, cidr=False,
_extra=None): extra=None,
if _extra is None: **kwargs):
if extra is None:
extra = {} extra = {}
else:
extra = _extra
extra['_private_only'] = private_only extra['_private_only'] = private_only
extra['_allow_reserved'] = allow_reserved extra['_allow_reserved'] = allow_reserved
extra['_cidr'] = cidr extra['_cidr'] = cidr
super().__init__(name, super().__init__(*args,
doc, extra=extra,
default=default, **kwargs)
default_multi=default_multi,
multi=multi,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)
def _validate(self, def _validate_cidr(self, value):
value: str,
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
# sometimes an ip term starts with a zero
# but this does not fit in some case, for example bind does not like it
if not isinstance(value, str):
raise ValueError(_('invalid string'))
if value.count('.') != 3:
raise ValueError()
cidr = self.impl_get_extra('_cidr')
if cidr:
if '/' not in value:
raise ValueError(_('must use CIDR notation'))
value_ = value.split('/')[0]
else:
value_ = value
for val in value_.split('.'):
if val.startswith("0") and len(val) > 1:
raise ValueError()
# 'standard' validation
try: try:
if not cidr: ip = ip_interface(value)
ip_address(value)
else:
ip = ip_interface(value)
except ValueError: except ValueError:
raise ValueError() raise ValueError()
if cidr: if ip.ip == ip.network.network_address:
valid_ip_netmask(str(ip.ip), str(ip.netmask)) raise ValueError(_("it's in fact a network address"))
elif ip.ip == ip.network.broadcast_address:
raise ValueError(_("it's in fact a broacast address"))
def _second_level_validation(self, def _validate_ip(self, value):
value, try:
warnings_only): ip_address(value)
except ValueError:
raise ValueError()
def validate(self,
value: str) -> None:
super().validate(value)
if self.impl_get_extra('_cidr'):
self._validate_cidr(value)
else:
self._validate_ip(value)
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
ip = ip_interface(value) ip = ip_interface(value)
if not self.impl_get_extra('_allow_reserved') and ip.is_reserved: if not self.impl_get_extra('_allow_reserved') and ip.is_reserved:
if warnings_only: if warnings_only:

View File

@ -34,14 +34,9 @@ class NetmaskOption(StrOption):
_type = 'netmask' _type = 'netmask'
_display_name = _('netmask address') _display_name = _('netmask address')
def _validate(self, def validate(self,
value: str, value: str) -> None:
option_bag: OptionBag, super().validate(value)
current_opt: Option=Undefined) -> None:
if not isinstance(value, str):
raise ValueError(_('invalid string'))
if value.count('.') != 3:
raise ValueError()
for val in value.split('.'): for val in value.split('.'):
if val.startswith("0") and len(val) > 1: if val.startswith("0") and len(val) > 1:
raise ValueError() raise ValueError()

View File

@ -20,44 +20,28 @@
# ____________________________________________________________ # ____________________________________________________________
from ipaddress import ip_address, ip_network from ipaddress import ip_address, ip_network
from ..setting import undefined
from ..i18n import _ from ..i18n import _
from .option import Option from .stroption import StrOption
class NetworkOption(Option): class NetworkOption(StrOption):
"represents the choice of a network" "represents the choice of a network"
__slots__ = tuple() __slots__ = tuple()
_type = 'network' _type = 'network'
_display_name = _('network address') _display_name = _('network address')
def __init__(self, def __init__(self,
name, *args,
doc, cidr=False,
default=None, **kwargs):
default_multi=None,
multi=False,
validators=None,
properties=None,
warnings_only=False,
cidr=False):
extra = {'_cidr': cidr} extra = {'_cidr': cidr}
super().__init__(name, super().__init__(*args,
doc, extra=extra,
default=default, **kwargs)
default_multi=default_multi,
multi=multi,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)
def _validate(self, def validate(self,
value, value: str) -> None:
*args, super().validate(value)
**kwargs):
if not isinstance(value, str):
raise ValueError(_('invalid string'))
if value.count('.') != 3: if value.count('.') != 3:
raise ValueError() raise ValueError()
cidr = self.impl_get_extra('_cidr') cidr = self.impl_get_extra('_cidr')
@ -75,9 +59,9 @@ class NetworkOption(Option):
except ValueError: except ValueError:
raise ValueError() raise ValueError()
def _second_level_validation(self, def second_level_validation(self,
value, value: str,
warnings_only): warnings_only: bool) -> None:
if ip_network(value).network_address.is_reserved: if ip_network(value).network_address.is_reserved:
if warnings_only: if warnings_only:
msg = _("shouldn't be reserved network") msg = _("shouldn't be reserved network")

View File

@ -30,10 +30,3 @@ class PasswordOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = 'password' _type = 'password'
_display_name = _('password') _display_name = _('password')
def _validate(self,
value: str,
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, str):
raise ValueError(_('invalid string'))

View File

@ -20,7 +20,6 @@
# ____________________________________________________________ # ____________________________________________________________
import re import re
import sys import sys
from typing import Union
from ..setting import undefined, Undefined, OptionBag from ..setting import undefined, Undefined, OptionBag
from ..i18n import _ from ..i18n import _
@ -44,19 +43,13 @@ class PortOption(StrOption):
_display_name = _('port') _display_name = _('port')
def __init__(self, def __init__(self,
name, *args,
doc, allow_range: bool=False,
default=None, allow_zero: bool=False,
default_multi=None, allow_wellknown: bool=True,
multi=False, allow_registred: bool=True,
validators=None, allow_private: bool=False,
properties=None, **kwargs) -> None:
allow_range=False,
allow_zero=False,
allow_wellknown=True,
allow_registred=True,
allow_private=False,
warnings_only=False):
extra = {'_allow_range': allow_range, extra = {'_allow_range': allow_range,
'_min_value': None, '_min_value': None,
@ -81,24 +74,15 @@ class PortOption(StrOption):
if extra['_max_value'] is None: if extra['_max_value'] is None:
raise ValueError(_('max value is empty')) raise ValueError(_('max value is empty'))
super(PortOption, self).__init__(name, super().__init__(*args,
doc, extra=extra,
default=default, **kwargs)
default_multi=default_multi,
multi=multi,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)
def _validate(self, def validate(self,
value: Union[int,str], value: str) -> None:
option_bag: OptionBag, super().validate(value)
current_opt: Option=Undefined) -> None:
if not isinstance(value, str):
raise ValueError(_('invalid string'))
if self.impl_get_extra('_allow_range') and ":" in str(value): if self.impl_get_extra('_allow_range') and ":" in str(value):
value = str(value).split(':') value = value.split(':')
if len(value) != 2: if len(value) != 2:
raise ValueError(_('range must have two values only')) raise ValueError(_('range must have two values only'))
if not value[0] < value[1]: if not value[0] < value[1]:
@ -110,8 +94,16 @@ class PortOption(StrOption):
for val in value: for val in value:
if not self.port_re.search(val): if not self.port_re.search(val):
raise ValueError() raise ValueError()
def second_level_validation(self,
value: str,
warnings_only: bool) -> None:
for val in value.split(':'):
val = int(val) val = int(val)
if not self.impl_get_extra('_min_value') <= val <= self.impl_get_extra('_max_value'): if not self.impl_get_extra('_min_value') <= val <= self.impl_get_extra('_max_value'):
raise ValueError(_('must be an integer between {0} ' if warnings_only:
'and {1}').format(self.impl_get_extra('_min_value'), msg = 'should be between {0} and {1}'
self.impl_get_extra('_max_value'))) else:
msg = 'must be between {0} and {1}'
raise ValueError(_(msg).format(self.impl_get_extra('_min_value'),
self.impl_get_extra('_max_value')))

View File

@ -32,10 +32,8 @@ class StrOption(Option):
_type = 'string' _type = 'string'
_display_name = _('string') _display_name = _('string')
def _validate(self, def validate(self,
value: str, value: str) -> None:
option_bag: OptionBag,
current_opt: Option=Undefined) -> None:
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError() raise ValueError()
@ -47,11 +45,9 @@ UnicodeOption = StrOption
class RegexpOption(StrOption): class RegexpOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
def _validate(self, def validate(self,
value: Any, value: Any) -> None:
option_bag: OptionBag, super().validate(value)
current_opt: Option=Undefined) -> None:
super()._validate(value, option_bag, current_opt)
match = self._regexp.search(value) match = self._regexp.search(value)
if not match: if not match:
raise ValueError() raise ValueError()

View File

@ -19,31 +19,75 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
import re import re
from typing import Any, Optional, List, Dict
from ..setting import undefined, Undefined, OptionBag from ..setting import undefined, Undefined, OptionBag
from ..i18n import _ from ..i18n import _
from .option import Option from .option import Option, Calculation
from .stroption import StrOption
from .domainnameoption import DomainnameOption from .domainnameoption import DomainnameOption
from .portoption import PortOption
class URLOption(DomainnameOption): class URLOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
proto_re = re.compile(r'(http|https)://')
path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") path_re = re.compile(r"^[A-Za-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
_type = 'url' _type = 'url'
_display_name = _('URL') _display_name = _('URL')
def _validate(self, def __init__(self,
value: str, name: str,
option_bag: OptionBag, doc: str,
current_opt: Option=Undefined) -> None: default: Any=undefined,
if not isinstance(value, str): default_multi: Any=None,
raise ValueError(_('invalid string')) multi: bool=False,
match = self.proto_re.search(value) validators: Optional[List[Calculation]]=None,
if not match: properties: Optional[List[str]]=None,
warnings_only: bool=False,
extra: Optional[Dict]=None,
allow_ip: bool=False,
type: str='domainname',
allow_without_dot=False,
allow_range: bool=False,
allow_zero: bool=False,
allow_wellknown: bool=True,
allow_registred: bool=True,
allow_private: bool=False) -> None:
extra = {'_domainname': DomainnameOption(name,
doc,
allow_ip=allow_ip,
type=type,
allow_without_dot=allow_without_dot),
'_port': PortOption(name,
doc,
allow_range=allow_range,
allow_zero=allow_zero,
allow_wellknown=allow_wellknown,
allow_registred=allow_registred,
allow_private=allow_private)}
super().__init__(name,
doc,
default=default,
default_multi=default_multi,
multi=multi,
validators=validators,
properties=properties,
warnings_only=warnings_only,
extra=extra)
def _get_domain_port_files(self,
value: str) -> (str, str):
if value.startswith('http://'):
type = 'http'
value = value[7:]
elif value.startswith('https://'):
type = 'https'
value = value[8:]
else:
raise ValueError(_('must start with http:// or ' raise ValueError(_('must start with http:// or '
'https://')) 'https://'))
value = value[len(match.group(0)):]
# get domain/files # get domain/files
splitted = value.split('/', 1) splitted = value.split('/', 1)
if len(splitted) == 1: if len(splitted) == 1:
@ -55,20 +99,31 @@ class URLOption(DomainnameOption):
splitted = domain.split(':', 1) splitted = domain.split(':', 1)
if len(splitted) == 1: if len(splitted) == 1:
domain = splitted[0] domain = splitted[0]
port = 0 port = {'http': '80',
'https': '443'}[type]
else: else:
domain, port = splitted domain, port = splitted
if not 0 <= int(port) <= 65535: return domain, port, files
raise ValueError(_('port must be an between 0 and '
'65536')) def validate(self,
value: str) -> None:
super().validate(value)
domain, port, files = self._get_domain_port_files(value)
# validate port
portoption = self.impl_get_extra('_port')
portoption.validate(port)
# validate domainname # validate domainname
super(URLOption, self)._validate(domain, domainnameoption = self.impl_get_extra('_domainname')
option_bag, domainnameoption.validate(domain)
current_opt) # validate files
super(URLOption, self)._second_level_validation(domain, False)
# validate file
if files is not None and files != '' and not self.path_re.search(files): if files is not None and files != '' and not self.path_re.search(files):
raise ValueError(_('must ends with a valid resource name')) raise ValueError(_('must ends with a valid resource name'))
def _second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
pass domain, port, files = self._get_domain_port_files(value)
# validate port
portoption = self.impl_get_extra('_port')
portoption.second_level_validation(port, warnings_only)
# validate domainname
domainnameoption = self.impl_get_extra('_domainname')
domainnameoption.second_level_validation(domain, warnings_only)