refactor DomainnameOption

add options EmailOption and URLOption
This commit is contained in:
Emmanuel Garette 2013-09-30 19:41:56 +02:00
parent 4fa33e1a6c
commit 1a294e3d09
2 changed files with 122 additions and 24 deletions

View File

@ -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'

View File

@ -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):