refactor DomainnameOption
add options EmailOption and URLOption
This commit is contained in:
parent
4fa33e1a6c
commit
1a294e3d09
|
@ -2,7 +2,7 @@ import autopath
|
|||
from py.test import raises
|
||||
|
||||
from tiramisu.config import Config
|
||||
from tiramisu.option import DomainnameOption, OptionDescription
|
||||
from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription
|
||||
|
||||
|
||||
def test_domainname():
|
||||
|
@ -14,11 +14,12 @@ def test_domainname():
|
|||
c.d = 'toto.com'
|
||||
raises(ValueError, "c.d = 'toto'")
|
||||
c.d = 'toto3.com'
|
||||
c.d = 'toto3.3la'
|
||||
raises(ValueError, "c.d = 'toto3.3la'")
|
||||
raises(ValueError, "c.d = '3toto.com'")
|
||||
c.d = 'toto.co3'
|
||||
raises(ValueError, "c.d = 'toto.co3'")
|
||||
raises(ValueError, "c.d = 'toto_super.com'")
|
||||
c.d = 'toto-.com'
|
||||
raises(ValueError, "c.d = 'toto..com'")
|
||||
|
||||
|
||||
def test_domainname_netbios():
|
||||
|
@ -41,3 +42,30 @@ def test_domainname_hostname():
|
|||
raises(ValueError, "c.d = 'toto.com'")
|
||||
c.d = 'toto'
|
||||
c.d = 'domainnametoolong'
|
||||
|
||||
|
||||
def test_email():
|
||||
e = EmailOption('e', '')
|
||||
od = OptionDescription('a', '', [e])
|
||||
c = Config(od)
|
||||
c.read_write()
|
||||
c.e = 'root@foo.com'
|
||||
raises(ValueError, "c.e = 'root'")
|
||||
raises(ValueError, "c.e = 'root@domain'")
|
||||
|
||||
|
||||
def test_url():
|
||||
u = URLOption('u', '')
|
||||
od = OptionDescription('a', '', [u])
|
||||
c = Config(od)
|
||||
c.read_write()
|
||||
c.u = 'http://foo.com'
|
||||
c.u = 'https://foo.com'
|
||||
c.u = 'https://foo.com/'
|
||||
raises(ValueError, "c.u = 'ftp://foo.com'")
|
||||
c.u = 'https://foo.com/index.html'
|
||||
c.u = 'https://foo.com/index.html?var=value&var2=val2'
|
||||
raises(ValueError, "c.u = 'https://foo.com/index\\n.html'")
|
||||
c.u = 'https://foo.com:8443'
|
||||
c.u = 'https://foo.com:8443/'
|
||||
c.u = 'https://foo.com:8443/index.html'
|
||||
|
|
|
@ -968,20 +968,35 @@ class DomainnameOption(Option):
|
|||
domainname:
|
||||
fqdn: with tld, not supported yet
|
||||
"""
|
||||
__slots__ = ('_type', '_allow_ip')
|
||||
__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
|
||||
_opt_type = 'domainname'
|
||||
|
||||
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):
|
||||
warnings_only=False, allow_without_dot=False):
|
||||
if type_ not in ['netbios', 'hostname', 'domainname']:
|
||||
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
|
||||
self._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'))
|
||||
self._allow_ip = allow_ip
|
||||
self._allow_without_dot = allow_without_dot
|
||||
end = ''
|
||||
extrachar = ''
|
||||
if self._type == 'netbios':
|
||||
length = 14
|
||||
elif self._type == 'hostname':
|
||||
length = 62
|
||||
elif self._type == 'domainname':
|
||||
length = 62
|
||||
extrachar = '\.'
|
||||
end = '+[a-z]*'
|
||||
self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-]{{,{0}}}{1}){2}$'
|
||||
''.format(length, extrachar, end))
|
||||
super(DomainnameOption, self).__init__(name, doc, default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
|
@ -1000,27 +1015,82 @@ class DomainnameOption(Option):
|
|||
return
|
||||
except ValueError:
|
||||
pass
|
||||
if self._type == 'netbios':
|
||||
length = 15
|
||||
extrachar = ''
|
||||
elif self._type == 'hostname':
|
||||
length = 63
|
||||
extrachar = ''
|
||||
elif self._type == 'domainname':
|
||||
length = 255
|
||||
extrachar = '\.'
|
||||
if '.' not in value:
|
||||
raise ValueError(_("invalid value for {0}, must have dot"
|
||||
"").format(self._name))
|
||||
if len(value) > length:
|
||||
raise ValueError(_("invalid domainname's length for"
|
||||
" {0} (max {1})").format(self._name, length))
|
||||
if len(value) == 1:
|
||||
if self._type == 'domainname' and not self._allow_without_dot and \
|
||||
'.' not in value:
|
||||
raise ValueError(_("invalid domainname for {0}, must have dot"
|
||||
"").format(self._name))
|
||||
if len(value) > 255:
|
||||
raise ValueError(_("invalid domainname's length for"
|
||||
" {0} (max 255)").format(self._name))
|
||||
if len(value) < 2:
|
||||
raise ValueError(_("invalid domainname's length for {0} (min 2)"
|
||||
"").format(self._name))
|
||||
regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
|
||||
if re.match(regexp, value) is None:
|
||||
raise ValueError(_('invalid domainname'))
|
||||
if not self._domain_re.search(value):
|
||||
raise ValueError(_('invalid domainname: {0}'.format(self._name)))
|
||||
|
||||
|
||||
class EmailOption(DomainnameOption):
|
||||
__slots__ = tuple()
|
||||
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['type_'] = 'domainname'
|
||||
kwargs['allow_ip'] = False
|
||||
kwargs['allow_without_dot'] = False
|
||||
super(EmailOption, self).__init__(*args, **kwargs)
|
||||
|
||||
def _validate(self, value):
|
||||
splitted = value.split('@', 1)
|
||||
try:
|
||||
username, domain = splitted
|
||||
except ValueError:
|
||||
raise ValueError(_('invalid email address, should contains one @ '
|
||||
'for {0}').format(self._name))
|
||||
if not self.username_re.search(username):
|
||||
raise ValueError(_('invalid username in email address for {0}').format(self._name))
|
||||
super(EmailOption, self)._validate(domain)
|
||||
|
||||
|
||||
class URLOption(DomainnameOption):
|
||||
__slots__ = tuple()
|
||||
proto_re = re.compile(r'(http|https)://')
|
||||
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['type_'] = 'domainname'
|
||||
kwargs['allow_ip'] = False
|
||||
kwargs['allow_without_dot'] = False
|
||||
super(URLOption, self).__init__(*args, **kwargs)
|
||||
|
||||
def _validate(self, value):
|
||||
match = self.proto_re.search(value)
|
||||
if not match:
|
||||
raise ValueError(_('invalid url, should start with http:// or '
|
||||
'https:// for {0}').format(self._name))
|
||||
value = value[len(match.group(0)):]
|
||||
# get domain/files
|
||||
splitted = value.split('/', 1)
|
||||
try:
|
||||
domain, files = splitted
|
||||
except ValueError:
|
||||
domain = value
|
||||
files = None
|
||||
# if port in domain
|
||||
splitted = domain.split(':', 1)
|
||||
try:
|
||||
domain, port = splitted
|
||||
|
||||
except ValueError:
|
||||
domain = splitted[0]
|
||||
port = 0
|
||||
if not 0 <= int(port) <= 65535:
|
||||
raise ValueError(_('port must be an between 0 and 65536'))
|
||||
# validate domainname
|
||||
super(URLOption, self)._validate(domain)
|
||||
# validate file
|
||||
if files is not None and files != '' and not self.path_re.search(files):
|
||||
raise ValueError(_('invalid url, should endswith with filename for'
|
||||
' {0}').format(self._name))
|
||||
|
||||
|
||||
class OptionDescription(BaseOption):
|
||||
|
|
Loading…
Reference in New Issue