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 py.test import raises
|
||||||
|
|
||||||
from tiramisu.config import Config
|
from tiramisu.config import Config
|
||||||
from tiramisu.option import DomainnameOption, OptionDescription
|
from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription
|
||||||
|
|
||||||
|
|
||||||
def test_domainname():
|
def test_domainname():
|
||||||
|
@ -14,11 +14,12 @@ def test_domainname():
|
||||||
c.d = 'toto.com'
|
c.d = 'toto.com'
|
||||||
raises(ValueError, "c.d = 'toto'")
|
raises(ValueError, "c.d = 'toto'")
|
||||||
c.d = 'toto3.com'
|
c.d = 'toto3.com'
|
||||||
c.d = 'toto3.3la'
|
raises(ValueError, "c.d = 'toto3.3la'")
|
||||||
raises(ValueError, "c.d = '3toto.com'")
|
raises(ValueError, "c.d = '3toto.com'")
|
||||||
c.d = 'toto.co3'
|
raises(ValueError, "c.d = 'toto.co3'")
|
||||||
raises(ValueError, "c.d = 'toto_super.com'")
|
raises(ValueError, "c.d = 'toto_super.com'")
|
||||||
c.d = 'toto-.com'
|
c.d = 'toto-.com'
|
||||||
|
raises(ValueError, "c.d = 'toto..com'")
|
||||||
|
|
||||||
|
|
||||||
def test_domainname_netbios():
|
def test_domainname_netbios():
|
||||||
|
@ -41,3 +42,30 @@ def test_domainname_hostname():
|
||||||
raises(ValueError, "c.d = 'toto.com'")
|
raises(ValueError, "c.d = 'toto.com'")
|
||||||
c.d = 'toto'
|
c.d = 'toto'
|
||||||
c.d = 'domainnametoolong'
|
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:
|
domainname:
|
||||||
fqdn: with tld, not supported yet
|
fqdn: with tld, not supported yet
|
||||||
"""
|
"""
|
||||||
__slots__ = ('_type', '_allow_ip')
|
__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
|
||||||
_opt_type = 'domainname'
|
_opt_type = 'domainname'
|
||||||
|
|
||||||
def __init__(self, name, doc, default=None, default_multi=None,
|
def __init__(self, name, doc, default=None, default_multi=None,
|
||||||
requires=None, multi=False, callback=None,
|
requires=None, multi=False, callback=None,
|
||||||
callback_params=None, validator=None, validator_params=None,
|
callback_params=None, validator=None, validator_params=None,
|
||||||
properties=None, allow_ip=False, type_='domainname',
|
properties=None, allow_ip=False, type_='domainname',
|
||||||
warnings_only=False):
|
warnings_only=False, allow_without_dot=False):
|
||||||
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_))
|
||||||
self._type = type_
|
self._type = type_
|
||||||
if allow_ip not in [True, False]:
|
if allow_ip not in [True, False]:
|
||||||
raise ValueError(_('allow_ip must be a boolean'))
|
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_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,
|
super(DomainnameOption, self).__init__(name, doc, default=default,
|
||||||
default_multi=default_multi,
|
default_multi=default_multi,
|
||||||
callback=callback,
|
callback=callback,
|
||||||
|
@ -1000,27 +1015,82 @@ class DomainnameOption(Option):
|
||||||
return
|
return
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
if self._type == 'netbios':
|
if self._type == 'domainname' and not self._allow_without_dot and \
|
||||||
length = 15
|
'.' not in value:
|
||||||
extrachar = ''
|
raise ValueError(_("invalid domainname for {0}, must have dot"
|
||||||
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))
|
"").format(self._name))
|
||||||
if len(value) > length:
|
if len(value) > 255:
|
||||||
raise ValueError(_("invalid domainname's length for"
|
raise ValueError(_("invalid domainname's length for"
|
||||||
" {0} (max {1})").format(self._name, length))
|
" {0} (max 255)").format(self._name))
|
||||||
if len(value) == 1:
|
if len(value) < 2:
|
||||||
raise ValueError(_("invalid domainname's length for {0} (min 2)"
|
raise ValueError(_("invalid domainname's length for {0} (min 2)"
|
||||||
"").format(self._name))
|
"").format(self._name))
|
||||||
regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
|
if not self._domain_re.search(value):
|
||||||
if re.match(regexp, value) is None:
|
raise ValueError(_('invalid domainname: {0}'.format(self._name)))
|
||||||
raise ValueError(_('invalid domainname'))
|
|
||||||
|
|
||||||
|
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):
|
class OptionDescription(BaseOption):
|
||||||
|
|
Loading…
Reference in New Issue