Add URIOpt

This change add URIOpt which validates string as URI.

Closes-Bug: #1500398
Change-Id: Ie8736b8654b9feb2a2b174159f08dbea03568d84
This commit is contained in:
Masaki Matsushita 2015-09-28 20:28:28 +09:00
parent bb0f7e3880
commit 45ee2bed52
7 changed files with 83 additions and 3 deletions

View File

@ -1268,6 +1268,19 @@ class HostnameOpt(Opt):
**kwargs)
class URIOpt(Opt):
"""Opt with URI type
Option with ``type`` :class:`oslo_config.types.URI`
.. versionadded:: 3.12
"""
def __init__(self, name, **kwargs):
super(URIOpt, self).__init__(name, type=types.URI(), **kwargs)
class MultiOpt(Opt):
"""Multi-value option.

View File

@ -77,9 +77,8 @@ def _format_defaults(opt):
default_str = str(opt.sample_default)
elif opt.default is None:
default_str = '<None>'
elif (isinstance(opt, cfg.StrOpt) or
isinstance(opt, cfg.IPOpt) or
isinstance(opt, cfg.HostnameOpt)):
elif (isinstance(opt, (cfg.StrOpt, cfg.IPOpt,
cfg.HostnameOpt, cfg.URIOpt))):
default_str = opt.default
elif isinstance(opt, cfg.BoolOpt):
default_str = str(opt.default).lower()

View File

@ -494,6 +494,13 @@ class CliOptsTestCase(BaseTestCase):
('port_arg_deprecated_group_and_name',
dict(opt_class=cfg.PortOpt, default=None,
cli_args=['--old-oof=80'], value=80, deps=('oof', 'old'))),
('uri_default',
dict(opt_class=cfg.URIOpt, default='http://example.com',
cli_args=[], value='http://example.com', deps=(None, None))),
('uri_arg',
dict(opt_class=cfg.URIOpt, default=None,
cli_args=['--foo', 'http://example.com'],
value='http://example.com', deps=(None, None))),
('multistr_default',
dict(opt_class=cfg.MultiStrOpt, default=['bar'], cli_args=[],
value=['bar'], deps=(None, None))),
@ -659,6 +666,17 @@ class PositionalTestCase(BaseTestCase):
def test_positional_port_arg(self):
self._do_pos_test(cfg.PortOpt, None, ['443'], 443)
def test_positional_uri_default(self):
self._do_pos_test(cfg.URIOpt, 'http://example.com', [],
'http://example.com')
def test_positional_uri_none_default(self):
self._do_pos_test(cfg.URIOpt, None, [], None)
def test_positional_uri_arg(self):
self._do_pos_test(cfg.URIOpt, None, ['http://example.com'],
'http://example.com')
def test_positional_multistr_none_default(self):
self._do_pos_test(cfg.MultiStrOpt, None, [], None)

View File

@ -143,6 +143,9 @@ class GeneratorTestCase(base.BaseTestCase):
'hostname_opt': cfg.HostnameOpt('hostname_opt',
default='compute01.nova.site1',
help='a hostname'),
'uri_opt': cfg.URIOpt('uri_opt',
default='http://example.com',
help='a URI'),
'multi_opt': cfg.MultiStrOpt('multi_opt',
default=['1', '2', '3'],
help='multiple strings'),

View File

@ -637,3 +637,15 @@ class HostnameTypeTests(TypeTestHelper, unittest.TestCase):
self.assertEqual(255, len(test_str))
self.assertInvalid(test_str)
self.assertConvertedEqual(test_str[:-2])
class URITypeTests(TypeTestHelper, unittest.TestCase):
type = types.URI()
def test_uri(self):
self.assertConvertedValue('http://example.com', 'http://example.com')
self.assertInvalid('invalid') # it doesn't include a scheme
self.assertInvalid('http://') # it doesn't include an authority
def test_repr(self):
self.assertEqual('URI', repr(types.URI()))

View File

@ -25,6 +25,7 @@ import warnings
import abc
import netaddr
import rfc3986
import six
@ -655,3 +656,36 @@ class Hostname(ConfigType):
def _formatter(self, value):
return value
class URI(ConfigType):
"""URI type
Represents URI. Value will be validated as RFC 3986.
:param type_name: Type name to be used in the sample config file.
"""
def __init__(self, type_name='uri value'):
super(URI, self).__init__(type_name=type_name)
def __call__(self, value):
if not rfc3986.is_valid_uri(value, require_scheme=True,
require_authority=True):
raise ValueError('invalid URI: %r' % value)
self.value = value
return value
def __repr__(self):
return 'URI'
def __eq__(self, other):
return (
(self.__class__ == other.__class__) and
(self.value == other.value)
)
def _formatter(self, value):
return value

View File

@ -7,3 +7,4 @@ netaddr!=0.7.16,>=0.7.12 # BSD
six>=1.9.0 # MIT
stevedore>=1.10.0 # Apache-2.0
oslo.i18n>=2.1.0 # Apache-2.0
rfc3986>=0.2.0 # Apache-2.0