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