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

View File

@ -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"
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) > length:
if len(value) > 255:
raise ValueError(_("invalid domainname's length for"
" {0} (max {1})").format(self._name, length))
if len(value) == 1:
" {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):