Rev 5944: (vila) Implement config.Option and its associated registry. (Vincent Ladeuil) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue May 31 20:22:28 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5944 [merge]
revision-id: pqm at pqm.ubuntu.com-20110531202224-lq6h1sol9sjr29oe
parent: pqm at pqm.ubuntu.com-20110531194327-pjelx43boom8r26y
parent: v.ladeuil+lp at free.fr-20110531192659-pn34qrm22ao9fvmu
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-05-31 20:22:24 +0000
message:
(vila) Implement config.Option and its associated registry. (Vincent Ladeuil)
modified:
bzrlib/config.py config.py-20051011043216-070c74f4e9e338e8
bzrlib/tests/test_config.py testconfig.py-20051011041908-742d0c15d8d8c8eb
doc/developers/configuration.txt configuration.txt-20110408142435-korjxxnskvq44sta-1
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py 2011-05-31 19:43:27 +0000
+++ b/bzrlib/config.py 2011-05-31 20:22:24 +0000
@@ -2092,8 +2092,35 @@
self._transport.put_file(self._filename, out_file)
+class Option(object):
+ """An option definition.
+
+ The option *values* are stored in config files and found in sections.
+
+ Here we define various properties about the option itself, its default
+ value, in which config files it can be stored, etc (TBC).
+ """
+
+ def __init__(self, name, default=None):
+ self.name = name
+ self.default = default
+
+ def get_default(self):
+ return self.default
+
+
+# Options registry
+
+option_registry = registry.Registry()
+
+
+# FIXME: Delete the following dummy option once we register the real ones
+# -- vila 20110515
+option_registry.register('foo', Option('foo'), help='Dummy option')
+
+
class Section(object):
- """A section defines a dict of options.
+ """A section defines a dict of option name => value.
This is merely a read-only dict which can add some knowledge about the
options. It is *not* a python dict object though and doesn't try to mimic
@@ -2540,7 +2567,7 @@
existence) require loading the store (even partially).
"""
# FIXME: No caching of options nor sections yet -- vila 20110503
-
+ value = None
# Ensuring lazy loading is achieved by delaying section matching (which
# implies querying the persistent storage) until it can't be avoided
# anymore by using callables to describe (possibly empty) section
@@ -2554,9 +2581,19 @@
for section in sections:
value = section.get(name)
if value is not None:
- return value
- # No definition was found
- return None
+ break
+ if value is not None:
+ break
+ if value is None:
+ # If the option is registered, it may provide a default value
+ try:
+ opt = option_registry.get(name)
+ except KeyError:
+ # Not registered
+ opt = None
+ if opt is not None:
+ value = opt.get_default()
+ return value
def _get_mutable_section(self):
"""Get the MutableSection for the Stack.
@@ -2802,7 +2839,6 @@
if not removed:
raise errors.NoSuchConfigOption(name)
-
# Test registries
#
# We need adapters that can build a Store or a Stack in a test context. Test
@@ -2810,7 +2846,8 @@
# themselves. The builder will receive a test instance and should return a
# ready-to-use store or stack. Plugins that define new store/stacks can also
# register themselves here to be tested against the tests defined in
-# bzrlib.tests.test_config.
+# bzrlib.tests.test_config. Note that the builder can be called multiple times
+# for the same tests.
# The registered object should be a callable receiving a test instance
# parameter (inheriting from tests.TestCaseWithTransport) and returning a Store
=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py 2011-05-31 19:43:27 +0000
+++ b/bzrlib/tests/test_config.py 2011-05-31 20:22:24 +0000
@@ -1917,6 +1917,62 @@
self.assertIs(None, bzrdir_config.get_default_stack_on())
+class TestOption(tests.TestCase):
+
+ def test_default_value(self):
+ opt = config.Option('foo', default='bar')
+ self.assertEquals('bar', opt.get_default())
+
+
+class TestOptionRegistry(tests.TestCase):
+
+ def setUp(self):
+ super(TestOptionRegistry, self).setUp()
+ # Always start with an empty registry
+ self.overrideAttr(config, 'option_registry', registry.Registry())
+ self.registry = config.option_registry
+
+ def test_register(self):
+ opt = config.Option('foo')
+ self.registry.register('foo', opt)
+ self.assertIs(opt, self.registry.get('foo'))
+
+ lazy_option = config.Option('lazy_foo')
+
+ def test_register_lazy(self):
+ self.registry.register_lazy('foo', self.__module__,
+ 'TestOptionRegistry.lazy_option')
+ self.assertIs(self.lazy_option, self.registry.get('foo'))
+
+ def test_registered_help(self):
+ opt = config.Option('foo')
+ self.registry.register('foo', opt, help='A simple option')
+ self.assertEquals('A simple option', self.registry.get_help('foo'))
+
+
+class TestRegisteredOptions(tests.TestCase):
+ """All registered options should verify some constraints."""
+
+ scenarios = [(key, {'option_name': key, 'option': option}) for key, option
+ in config.option_registry.iteritems()]
+
+ def setUp(self):
+ super(TestRegisteredOptions, self).setUp()
+ self.registry = config.option_registry
+
+ def test_proper_name(self):
+ # An option should be registered under its own name, this can't be
+ # checked at registration time for the lazy ones.
+ self.assertEquals(self.option_name, self.option.name)
+
+ def test_help_is_set(self):
+ option_help = self.registry.get_help(self.option_name)
+ self.assertNotEquals(None, option_help)
+ # Come on, think about the user, he really wants to know whst the
+ # option is about
+ self.assertNotEquals('', option_help)
+
+
class TestSection(tests.TestCase):
# FIXME: Parametrize so that all sections produced by Stores run these
@@ -2432,6 +2488,26 @@
conf_stack = config.Stack([conf])
self.assertEquals('bar', conf_stack.get('foo'))
+ def test_get_with_registered_default_value(self):
+ conf_stack = config.Stack([dict()])
+ opt = config.Option('foo', default='bar')
+ self.overrideAttr(config, 'option_registry', registry.Registry())
+ config.option_registry.register('foo', opt)
+ self.assertEquals('bar', conf_stack.get('foo'))
+
+ def test_get_without_registered_default_value(self):
+ conf_stack = config.Stack([dict()])
+ opt = config.Option('foo')
+ self.overrideAttr(config, 'option_registry', registry.Registry())
+ config.option_registry.register('foo', opt)
+ self.assertEquals(None, conf_stack.get('foo'))
+
+ def test_get_without_default_value_for_not_registered(self):
+ conf_stack = config.Stack([dict()])
+ opt = config.Option('foo')
+ self.overrideAttr(config, 'option_registry', registry.Registry())
+ self.assertEquals(None, conf_stack.get('foo'))
+
def test_get_first_definition(self):
conf1 = dict(foo='bar')
conf2 = dict(foo='baz')
@@ -2464,6 +2540,13 @@
in config.test_stack_builder_registry.iteritems()]
+class TestConcreteStacks(TestStackWithTransport):
+
+ def test_build_stack(self):
+ # Just a smoke test to help debug builders
+ stack = self.get_stack(self)
+
+
class TestStackSet(TestStackWithTransport):
def test_simple_set(self):
@@ -2495,12 +2578,6 @@
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
-class TestConcreteStacks(TestStackWithTransport):
-
- def test_build_stack(self):
- stack = self.get_stack(self)
-
-
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
def setUp(self):
=== modified file 'doc/developers/configuration.txt'
--- a/doc/developers/configuration.txt 2011-05-04 07:56:15 +0000
+++ b/doc/developers/configuration.txt 2011-05-18 08:47:16 +0000
@@ -3,10 +3,24 @@
A configuration option has:
-- a name: a valid python identifier (even if it's not used as an
+* a name: a valid python identifier (even if it's not used as an
identifier in python itself)
-- a value: a unicode string
+* a value: a unicode string
+
+Option
+------
+
+The Option object is used to define its properties:
+
+* name: a name: a valid python identifier (even if it's not used as an
+ identifier in python itself). This is also used to register the option.
+
+* default: the default value that Stack.get() should return if no
+ value can be found for the option.
+
+Since plugins should be able to lazily register options, the associated help
+is not part of the object but provided at registration time.
Sections
--------
@@ -14,9 +28,9 @@
Options are grouped into sections which share some properties with the well
known dict objects:
-- the key is the name,
-- you can get, set and remove an option,
-- the value is a unicode string.
+* the key is the name,
+* you can get, set and remove an option,
+* the value is a unicode string.
MutableSection is needed to set or remove an option, ReadOnlySection should
be used otherwise.
More information about the bazaar-commits
mailing list