Bug 1255450 - [mach] Implement 'wildcard' settings for enabling sections with user-defined options, r=gps

Some sections should support user-defined options. For example, in an [alias] section, the option names
are not well-defined, rather specified by the user. This patch allows user-defined option names for any
section that has a 'section.*' option defined. Even with 'section.*', option types are still well-defined.

MozReview-Commit-ID: L34W9v9Fy28

--HG--
extra : rebase_source : 9333f552edead9bf1cf464e28ef8fbbb9bed5597
This commit is contained in:
Andrew Halberstadt 2016-03-28 10:52:16 -04:00
Родитель 5167efa8e4
Коммит c105e8a3b4
3 изменённых файлов: 60 добавлений и 8 удалений

Просмотреть файл

@ -54,6 +54,26 @@ pairs to add to the setting's metadata. The following keys may be specified
in the ``extra`` dict:
* ``choices`` - A set of allowed values for the setting.
Wildcards
---------
Sometimes a section should allow arbitrarily defined options from the user, such
as the ``alias`` section mentioned above. To define a section like this, use ``*``
as the option name. For example:
.. parsed-literal::
('foo.*', 'string')
This allows configuration files like this:
.. parsed-literal::
[foo]
arbitrary1 = some string
arbitrary2 = some other string
Accessing Settings
==================

Просмотреть файл

@ -213,6 +213,9 @@ class ConfigSettings(collections.Mapping):
object.__setattr__(self, '_name', name)
object.__setattr__(self, '_settings', settings)
wildcard = any(s == '*' for s in self._settings)
object.__setattr__(self, '_wildcard', wildcard)
@property
def options(self):
try:
@ -220,11 +223,15 @@ class ConfigSettings(collections.Mapping):
except NoSectionError:
return []
def _validate(self, option, value):
if option not in self._settings:
def get_meta(self, option):
if option in self._settings:
return self._settings[option]
if self._wildcard:
return self._settings['*']
raise KeyError('Option not registered with provider: %s' % option)
meta = self._settings[option]
def _validate(self, option, value):
meta = self.get_meta(option)
meta['type_cls'].validate(value)
if 'choices' in meta and value not in meta['choices']:
@ -242,7 +249,7 @@ class ConfigSettings(collections.Mapping):
return self._config.has_option(self._name, k)
def __getitem__(self, k):
meta = self._settings[k]
meta = self.get_meta(k)
if self._config.has_option(self._name, k):
v = meta['type_cls'].from_config(self._config, self._name, k)
@ -255,11 +262,10 @@ class ConfigSettings(collections.Mapping):
self._validate(k, v)
return v
def __setitem__(self, k, v):
self._validate(k, v)
meta = self.get_meta(k)
meta = self._settings[k]
if not self._config.has_section(self._name):
self._config.add_section(self._name)
@ -416,7 +422,7 @@ class ConfigSettings(collections.Mapping):
def option_help(self, section, option):
"""Obtain the translated help messages for an option."""
meta = self[section]._settings[option]
meta = self[section].get_meta(option)
# Providers should always have an en-US translation. If they don't,
# they are coded wrong and this will raise.

Просмотреть файл

@ -88,6 +88,14 @@ class Provider4(object):
]
@SettingsProvider
class Provider5(object):
config_settings = [
('foo.*', 'string'),
('foo.bar', 'string'),
]
class TestConfigSettings(unittest.TestCase):
def test_empty(self):
s = ConfigSettings()
@ -211,6 +219,24 @@ class TestConfigSettings(unittest.TestCase):
foo.abc = 'b'
foo.xyz = 'y'
def test_wildcard_options(self):
s = ConfigSettings()
s.register_provider(Provider5)
foo = s.foo
self.assertIn('*', foo._settings)
self.assertNotIn('*', foo)
foo.baz = 'value1'
foo.bar = 'value2'
self.assertIn('baz', foo)
self.assertEqual(foo.baz, 'value1')
self.assertIn('bar', foo)
self.assertEqual(foo.bar, 'value2')
def test_file_reading_single(self):
temp = NamedTemporaryFile(mode='wt')
temp.write(CONFIG1)