Rev 6273: (vila) Switch ``bzr config`` to the stack-based config implementation in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

Patch Queue Manager pqm at pqm.ubuntu.com
Thu Nov 17 17:41:46 UTC 2011


At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 6273 [merge]
revision-id: pqm at pqm.ubuntu.com-20111117174145-2nb7jko9c5t7llcb
parent: pqm at pqm.ubuntu.com-20111117163543-1wumacqhy65ii76k
parent: v.ladeuil+lp at free.fr-20111117171609-pzs107pdm0vndlf9
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2011-11-17 17:41:45 +0000
message:
  (vila) Switch ``bzr config`` to the stack-based config implementation
   (Vincent Ladeuil)
modified:
  bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
  bzrlib/library_state.py        library_state.py-20100625053036-962zdkiik8k6m5jx-1
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/blackbox/test_config.py test_config.py-20100927150753-x6rf54uibd08r636-1
  bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
  bzrlib/tests/test_debug.py     test_debug.py-20090303053802-01e8mlv24odmpgix-1
  bzrlib/tests/test_msgeditor.py test_msgeditor.py-20051202041359-920315ec6011ee51
  doc/en/release-notes/bzr-2.5.txt bzr2.5.txt-20110708125756-587p0hpw7oke4h05-1
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2011-10-31 22:11:59 +0000
+++ b/bzrlib/config.py	2011-11-16 17:19:13 +0000
@@ -2632,9 +2632,13 @@
         # We re-use the dict-like object received
         self.options = options
 
-    def get(self, name, default=None):
+    def get(self, name, default=None, expand=True):
         return self.options.get(name, default)
 
+    def iter_option_names(self):
+        for k in self.options.iterkeys():
+            yield k
+
     def __repr__(self):
         # Mostly for debugging use
         return "<config.%s id=%s>" % (self.__class__.__name__, self.id)
@@ -2665,31 +2669,6 @@
         del self.options[name]
 
 
-class CommandLineSection(MutableSection):
-    """A section used to carry command line overrides for the config options."""
-
-    def __init__(self, opts=None):
-        if opts is None:
-            opts = {}
-        super(CommandLineSection, self).__init__('cmdline-overrides', opts)
-
-    def _reset(self):
-        # The dict should be cleared but not replaced so it can be shared.
-        self.options.clear()
-
-    def _from_cmdline(self, overrides):
-        # Reset before accepting new definitions
-        self._reset()
-        for over in overrides:
-            try:
-                name, value = over.split('=', 1)
-            except ValueError:
-                raise errors.BzrCommandError(
-                    gettext("Invalid '%s', should be of the form 'name=value'")
-                    % (over,))
-            self.set(name, value)
-
-
 class Store(object):
     """Abstract interface to persistent storage for configuration options."""
 
@@ -2734,14 +2713,14 @@
     def get_sections(self):
         """Returns an ordered iterable of existing sections.
 
-        :returns: An iterable of (name, dict).
+        :returns: An iterable of (store, section).
         """
         raise NotImplementedError(self.get_sections)
 
-    def get_mutable_section(self, section_name=None):
+    def get_mutable_section(self, section_id=None):
         """Returns the specified mutable section.
 
-        :param section_name: The section identifier
+        :param section_id: The section identifier
         """
         raise NotImplementedError(self.get_mutable_section)
 
@@ -2751,6 +2730,41 @@
                                     self.external_url())
 
 
+class CommandLineStore(Store):
+    "A store to carry command line overrides for the config options."""
+
+    def __init__(self, opts=None):
+        super(CommandLineStore, self).__init__()
+        if opts is None:
+            opts = {}
+        self.options = {}
+
+    def _reset(self):
+        # The dict should be cleared but not replaced so it can be shared.
+        self.options.clear()
+
+    def _from_cmdline(self, overrides):
+        # Reset before accepting new definitions
+        self._reset()
+        for over in overrides:
+            try:
+                name, value = over.split('=', 1)
+            except ValueError:
+                raise errors.BzrCommandError(
+                    gettext("Invalid '%s', should be of the form 'name=value'")
+                    % (over,))
+            self.options[name] = value
+
+    def external_url(self):
+        # Not an url but it makes debugging easier and it never needed
+        # otherwise
+        return 'cmdline'
+
+    def get_sections(self):
+        yield self,  self.readonly_section_class('cmdline_overrides',
+                                                 self.options)
+
+
 class IniFileStore(Store):
     """A config Store using ConfigObj for storage.
 
@@ -2833,7 +2847,7 @@
     def get_sections(self):
         """Get the configobj section in the file order.
 
-        :returns: An iterable of (name, dict).
+        :returns: An iterable of (store, section).
         """
         # We need a loaded store
         try:
@@ -2843,22 +2857,24 @@
             return
         cobj = self._config_obj
         if cobj.scalars:
-            yield self.readonly_section_class(None, cobj)
+            yield self, self.readonly_section_class(None, cobj)
         for section_name in cobj.sections:
-            yield self.readonly_section_class(section_name, cobj[section_name])
+            yield (self,
+                   self.readonly_section_class(section_name,
+                                               cobj[section_name]))
 
-    def get_mutable_section(self, section_name=None):
+    def get_mutable_section(self, section_id=None):
         # We need a loaded store
         try:
             self.load()
         except errors.NoSuchFile:
             # The file doesn't exist, let's pretend it was empty
             self._load_from_string('')
-        if section_name is None:
+        if section_id is None:
             section = self._config_obj
         else:
-            section = self._config_obj.setdefault(section_name, {})
-        return self.mutable_section_class(section_name, section)
+            section = self._config_obj.setdefault(section_id, {})
+        return self.mutable_section_class(section_id, section)
 
 
 # Note that LockableConfigObjStore inherits from ConfigObjStore because we need
@@ -2923,6 +2939,7 @@
         t = transport.get_transport_from_path(
             config_dir(), possible_transports=possible_transports)
         super(GlobalStore, self).__init__(t, 'bazaar.conf')
+        self.id = 'bazaar'
 
 
 class LocationStore(LockableIniFileStore):
@@ -2931,6 +2948,7 @@
         t = transport.get_transport_from_path(
             config_dir(), possible_transports=possible_transports)
         super(LocationStore, self).__init__(t, 'locations.conf')
+        self.id = 'locations'
 
 
 class BranchStore(IniFileStore):
@@ -2939,6 +2957,7 @@
         super(BranchStore, self).__init__(branch.control_transport,
                                           'branch.conf')
         self.branch = branch
+        self.id = 'branch'
 
     def lock_write(self, token=None):
         return self.branch.lock_write(token)
@@ -2978,9 +2997,9 @@
         # sections.
         sections = self.store.get_sections()
         # Walk the revisions in the order provided
-        for s in sections:
+        for store, s in sections:
             if self.match(s):
-                yield s
+                yield store, s
 
     def match(self, section):
         """Does the proposed section match.
@@ -3010,9 +3029,9 @@
         self.extra_path = extra_path
         self.locals = {'relpath': extra_path}
 
-    def get(self, name, default=None):
+    def get(self, name, default=None, expand=True):
         value = super(LocationSection, self).get(name, default)
-        if value is not None:
+        if value is not None and expand:
             policy_name = self.get(name + ':policy', None)
             policy = _policy_value.get(policy_name, POLICY_NONE)
             if policy == POLICY_APPENDPATH:
@@ -3049,7 +3068,7 @@
         all_sections = []
         # Filter out the no_name_section so _iter_for_location_by_parts can be
         # used (it assumes all sections have a name).
-        for section in self.store.get_sections():
+        for _, section in self.store.get_sections():
             if section.id is None:
                 no_name_section = section
             else:
@@ -3092,7 +3111,7 @@
             if ignore:
                 break
             # Finally, we have a valid section
-            yield section
+            yield self.store, section
 
 
 _option_ref_re = lazy_regex.lazy_compile('({[^{}]+})')
@@ -3115,7 +3134,7 @@
 class Stack(object):
     """A stack of configurations where an option can be defined"""
 
-    def __init__(self, sections_def, store=None, mutable_section_name=None):
+    def __init__(self, sections_def, store=None, mutable_section_id=None):
         """Creates a stack of sections with an optional store for changes.
 
         :param sections_def: A list of Section or callables that returns an
@@ -3125,13 +3144,13 @@
         :param store: The optional Store where modifications will be
             recorded. If none is specified, no modifications can be done.
 
-        :param mutable_section_name: The name of the MutableSection where
-            changes are recorded. This requires the ``store`` parameter to be
+        :param mutable_section_id: The id of the MutableSection where changes
+            are recorded. This requires the ``store`` parameter to be
             specified.
         """
         self.sections_def = sections_def
         self.store = store
-        self.mutable_section_name = mutable_section_name
+        self.mutable_section_id = mutable_section_id
 
     def get(self, name, expand=None):
         """Return the *first* option value found in the sections.
@@ -3156,13 +3175,8 @@
         # implies querying the persistent storage) until it can't be avoided
         # anymore by using callables to describe (possibly empty) section
         # lists.
-        for section_or_callable in self.sections_def:
-            # Each section can expand to multiple ones when a callable is used
-            if callable(section_or_callable):
-                sections = section_or_callable()
-            else:
-                sections = [section_or_callable]
-            for section in sections:
+        for sections in self.sections_def:
+            for store, section in sections():
                 value = section.get(name)
                 if value is not None:
                     break
@@ -3269,9 +3283,9 @@
         This is where we guarantee that the mutable section is lazily loaded:
         this means we won't load the corresponding store before setting a value
         or deleting an option. In practice the store will often be loaded but
-        this allows helps catching some programming errors.
+        this helps catching some programming errors.
         """
-        section = self.store.get_mutable_section(self.mutable_section_name)
+        section = self.store.get_mutable_section(self.mutable_section_id)
         return section
 
     def set(self, name, value):
@@ -3295,7 +3309,7 @@
     def _get_overrides(self):
         # Hack around library_state.initialize never called
         if bzrlib.global_state is not None:
-            return [bzrlib.global_state.cmdline_overrides]
+            return bzrlib.global_state.cmdline_overrides.get_sections()
         return []
 
 
@@ -3308,8 +3322,8 @@
     One assumption made here is that the daughter classes will all use Stores
     derived from LockableIniFileStore).
 
-    It implements set() by re-loading the store before applying the
-    modification and saving it.
+    It implements set() and remove () by re-loading the store before applying
+    the modification and saving it.
 
     The long term plan being to implement a single write by store to save
     all modifications, this class should not be used in the interim.
@@ -3322,6 +3336,13 @@
         # Force a write to persistent storage
         self.store.save()
 
+    def remove(self, name):
+        # Force a reload
+        self.store.unload()
+        super(_CompatibleStack, self).remove(name)
+        # Force a write to persistent storage
+        self.store.save()
+
 
 class GlobalStack(_CompatibleStack):
     """Global options only stack."""
@@ -3330,8 +3351,8 @@
         # Get a GlobalStore
         gstore = GlobalStore()
         super(GlobalStack, self).__init__(
-            [self._get_overrides, gstore.get_sections],
-            gstore)
+            [self._get_overrides, NameMatcher(gstore, 'DEFAULT').get_sections],
+            gstore, mutable_section_id='DEFAULT')
 
 
 class LocationStack(_CompatibleStack):
@@ -3342,12 +3363,16 @@
         
         :param location: A URL prefix to """
         lstore = LocationStore()
+        if location is not None:
+            location = urlutils.normalize_url(location)
+            if location.startswith('file://'):
+                location = urlutils.local_path_from_url(location)
         matcher = LocationMatcher(lstore, location)
         gstore = GlobalStore()
         super(LocationStack, self).__init__(
             [self._get_overrides,
-             matcher.get_sections, gstore.get_sections],
-            lstore)
+             matcher.get_sections, NameMatcher(gstore, 'DEFAULT').get_sections],
+            lstore, mutable_section_id=location)
 
 
 class BranchStack(_CompatibleStack):
@@ -3360,7 +3385,8 @@
         gstore = GlobalStore()
         super(BranchStack, self).__init__(
             [self._get_overrides,
-             matcher.get_sections, bstore.get_sections, gstore.get_sections],
+             matcher.get_sections, bstore.get_sections,
+             NameMatcher(gstore, 'DEFAULT').get_sections],
             bstore)
         self.branch = branch
 
@@ -3386,6 +3412,10 @@
             bstore)
         self.branch = branch
 
+# Use a an empty dict to initialize an empty configobj avoiding all
+# parsing and encoding checks
+_quoting_config = configobj.ConfigObj(
+    {}, encoding='utf-8', interpolation=False)
 
 class cmd_config(commands.Command):
     __doc__ = """Display, set or remove a configuration option.
@@ -3408,7 +3438,8 @@
     takes_options = [
         'directory',
         # FIXME: This should be a registry option so that plugins can register
-        # their own config files (or not) -- vila 20101002
+        # their own config files (or not) and will also address
+        # http://pad.lv/788991 -- vila 20101115
         commands.Option('scope', help='Reduce the scope to the specified'
                         ' configuration file',
                         type=unicode),
@@ -3452,53 +3483,44 @@
                 # Set the option value
                 self._set_config_option(name, value, directory, scope)
 
-    def _get_configs(self, directory, scope=None):
-        """Iterate the configurations specified by ``directory`` and ``scope``.
+    def _get_stack(self, directory, scope=None):
+        """Get the configuration stack specified by ``directory`` and ``scope``.
 
         :param directory: Where the configurations are derived from.
 
         :param scope: A specific config to start from.
         """
+        # FIXME: scope should allow access to plugin-specific stacks (even
+        # reduced to the plugin-specific store), related to
+        # http://pad.lv/788991 -- vila 2011-11-15
         if scope is not None:
             if scope == 'bazaar':
-                yield GlobalConfig()
+                return GlobalStack()
             elif scope == 'locations':
-                yield LocationConfig(directory)
+                return LocationStack(directory)
             elif scope == 'branch':
                 (_, br, _) = (
                     controldir.ControlDir.open_containing_tree_or_branch(
                         directory))
-                yield br.get_config()
+                return br.get_config_stack()
+            raise errors.NoSuchConfig(scope)
         else:
             try:
                 (_, br, _) = (
                     controldir.ControlDir.open_containing_tree_or_branch(
                         directory))
-                yield br.get_config()
+                return br.get_config_stack()
             except errors.NotBranchError:
-                yield LocationConfig(directory)
-                yield GlobalConfig()
+                return LocationStack(directory)
 
     def _show_value(self, name, directory, scope):
-        displayed = False
-        for c in self._get_configs(directory, scope):
-            if displayed:
-                break
-            for (oname, value, section, conf_id, parser) in c._get_options():
-                if name == oname:
-                    # Display only the first value and exit
-
-                    # FIXME: We need to use get_user_option to take policies
-                    # into account and we need to make sure the option exists
-                    # too (hence the two for loops), this needs a better API
-                    # -- vila 20101117
-                    value = c.get_user_option(name)
-                    # Quote the value appropriately
-                    value = parser._quote(value)
-                    self.outf.write('%s\n' % (value,))
-                    displayed = True
-                    break
-        if not displayed:
+        conf = self._get_stack(directory, scope)
+        value = conf.get(name, expand=True)
+        if value is not None:
+            # Quote the value appropriately
+            value = _quoting_config._quote(value)
+            self.outf.write('%s\n' % (value,))
+        else:
             raise errors.NoSuchConfigOption(name)
 
     def _show_matching_options(self, name, directory, scope):
@@ -3507,55 +3529,43 @@
         # avoid the delay introduced by the lazy regexp.  But, we still do
         # want the nicer errors raised by lazy_regex.
         name._compile_and_collapse()
-        cur_conf_id = None
+        cur_store_id = None
         cur_section = None
-        for c in self._get_configs(directory, scope):
-            for (oname, value, section, conf_id, parser) in c._get_options():
-                if name.search(oname):
-                    if cur_conf_id != conf_id:
-                        # Explain where the options are defined
-                        self.outf.write('%s:\n' % (conf_id,))
-                        cur_conf_id = conf_id
-                        cur_section = None
-                    if (section not in (None, 'DEFAULT')
-                        and cur_section != section):
-                        # Display the section if it's not the default (or only)
-                        # one.
-                        self.outf.write('  [%s]\n' % (section,))
-                        cur_section = section
-                    self.outf.write('  %s = %s\n' % (oname, value))
+        conf = self._get_stack(directory, scope)
+        for sections in conf.sections_def:
+            for store, section in sections():
+                for oname in section.iter_option_names():
+                    if name.search(oname):
+                        if cur_store_id != store.id:
+                            # Explain where the options are defined
+                            self.outf.write('%s:\n' % (store.id,))
+                            cur_store_id = store.id
+                            cur_section = None
+                        if (section.id not in (None, 'DEFAULT')
+                            and cur_section != section.id):
+                            # Display the section if it's not the default (or
+                            # only) one.
+                            self.outf.write('  [%s]\n' % (section.id,))
+                            cur_section = section.id
+                        value = section.get(oname, expand=False)
+                        value = _quoting_config._quote(value)
+                        self.outf.write('  %s = %s\n' % (oname, value))
 
     def _set_config_option(self, name, value, directory, scope):
-        for conf in self._get_configs(directory, scope):
-            conf.set_user_option(name, value)
-            break
-        else:
-            raise errors.NoSuchConfig(scope)
+        conf = self._get_stack(directory, scope)
+        conf.set(name, value)
 
     def _remove_config_option(self, name, directory, scope):
         if name is None:
             raise errors.BzrCommandError(
                 '--remove expects an option to remove.')
-        removed = False
-        for conf in self._get_configs(directory, scope):
-            for (section_name, section, conf_id) in conf._get_sections():
-                if scope is not None and conf_id != scope:
-                    # Not the right configuration file
-                    continue
-                if name in section:
-                    if conf_id != conf.config_id():
-                        conf = self._get_configs(directory, conf_id).next()
-                    # We use the first section in the first config where the
-                    # option is defined to remove it
-                    conf.remove_user_option(name, section_name)
-                    removed = True
-                    break
-            break
-        else:
-            raise errors.NoSuchConfig(scope)
-        if not removed:
+        conf = self._get_stack(directory, scope)
+        try:
+            conf.remove(name)
+        except KeyError:
             raise errors.NoSuchConfigOption(name)
 
+
 # Test registries
 #
 # We need adapters that can build a Store or a Stack in a test context. Test

=== modified file 'bzrlib/library_state.py'
--- a/bzrlib/library_state.py	2011-09-26 15:40:02 +0000
+++ b/bzrlib/library_state.py	2011-11-16 15:57:14 +0000
@@ -67,7 +67,7 @@
         self._trace = trace
         # There is no overrides by default, they are set later when the command
         # arguments are parsed.
-        self.cmdline_overrides = config.CommandLineSection()
+        self.cmdline_overrides = config.CommandLineStore()
         self.started = False
 
     def __enter__(self):

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2011-11-07 10:14:38 +0000
+++ b/bzrlib/tests/__init__.py	2011-11-16 15:57:14 +0000
@@ -999,7 +999,7 @@
         self._cleanEnvironment()
         if bzrlib.global_state is not None:
             self.overrideAttr(bzrlib.global_state, 'cmdline_overrides',
-                              config.CommandLineSection())
+                              config.CommandLineStore())
         self._silenceUI()
         self._startLogFile()
         self._benchcalls = []

=== modified file 'bzrlib/tests/blackbox/test_config.py'
--- a/bzrlib/tests/blackbox/test_config.py	2011-02-22 12:59:14 +0000
+++ b/bzrlib/tests/blackbox/test_config.py	2011-11-16 15:57:14 +0000
@@ -96,18 +96,22 @@
             ''')
 
     def test_list_all_values(self):
+        # FIXME: we should register the option as a list or it's displayed as
+        # astring and as such, quoted.
         self.bazaar_config.set_user_option('list', [1, 'a', 'with, a comma'])
         script.run_script(self, '''\
             $ bzr config -d tree
             bazaar:
-              list = 1, a, "with, a comma"
+              list = '1, a, "with, a comma"'
             ''')
 
     def test_list_value_only(self):
+        # FIXME: we should register the option as a list or it's displayed as
+        # astring and as such, quoted.
         self.bazaar_config.set_user_option('list', [1, 'a', 'with, a comma'])
         script.run_script(self, '''\
             $ bzr config -d tree list
-            1, a, "with, a comma"
+            '1, a, "with, a comma"'
             ''')
 
     def test_bazaar_config(self):
@@ -189,7 +193,7 @@
         # We need to delete the locations definition that overrides the branch
         # one
         script.run_script(self, '''\
-            $ bzr config -d tree --remove file
+            $ bzr config -d tree --scope locations --remove file
             $ bzr config -d tree file
             branch
             ''')
@@ -291,7 +295,7 @@
 
     def test_branch_config_default(self):
         script.run_script(self, '''\
-            $ bzr config -d tree --remove file
+            $ bzr config -d tree --scope locations --remove file
             $ bzr config -d tree --all file
             branch:
               file = branch
@@ -316,7 +320,7 @@
               file = bazaar
             ''')
         script.run_script(self, '''\
-            $ bzr config -d tree --remove file
+            $ bzr config -d tree --scope locations --remove file
             $ bzr config -d tree --all file
             bazaar:
               file = bazaar

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2011-10-31 22:11:59 +0000
+++ b/bzrlib/tests/test_config.py	2011-11-16 17:19:13 +0000
@@ -2565,9 +2565,6 @@
     scenarios = [('mutable',
                   {'get_section':
                        lambda opts: config.MutableSection('myID', opts)},),
-                 ('cmdline',
-                  {'get_section':
-                       lambda opts: config.CommandLineSection(opts)},),
         ]
 
     def test_set(self):
@@ -2615,43 +2612,52 @@
         self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
 
 
-class TestCommandLineSection(tests.TestCase):
+class TestCommandLineStore(tests.TestCase):
 
     def setUp(self):
-        super(TestCommandLineSection, self).setUp()
-        self.section = config.CommandLineSection()
+        super(TestCommandLineStore, self).setUp()
+        self.store = config.CommandLineStore()
+
+    def get_section(self):
+        """Get the unique section for the command line overrides."""
+        sections = list(self.store.get_sections())
+        self.assertLength(1, sections)
+        store, section = sections[0]
+        self.assertEquals(self.store, store)
+        return section
 
     def test_no_override(self):
-        self.section._from_cmdline([])
-        # FIXME: we want some iterator over all options, failing that, we peek
-        # under the cover -- vila 2011-09026
-        self.assertLength(0, self.section.options)
+        self.store._from_cmdline([])
+        section = self.get_section()
+        self.assertLength(0, list(section.iter_option_names()))
 
     def test_simple_override(self):
-        self.section._from_cmdline(['a=b'])
-        self.assertEqual('b', self.section.get('a'))
+        self.store._from_cmdline(['a=b'])
+        section = self.get_section()
+        self.assertEqual('b', section.get('a'))
 
     def test_list_override(self):
-        self.section._from_cmdline(['l=1,2,3'])
-        val = self.section.get('l')
+        self.store._from_cmdline(['l=1,2,3'])
+        val = self.get_section().get('l')
         self.assertEqual('1,2,3', val)
-        # Reminder: lists should registered as such explicitely, otherwise the
-        # conversion needs to be done afterwards.
+        # Reminder: lists should be registered as such explicitely, otherwise
+        # the conversion needs to be done afterwards.
         self.assertEqual(['1', '2', '3'], config.list_from_store(val))
 
     def test_multiple_overrides(self):
-        self.section._from_cmdline(['a=b', 'x=y'])
-        self.assertEquals('b', self.section.get('a'))
-        self.assertEquals('y', self.section.get('x'))
+        self.store._from_cmdline(['a=b', 'x=y'])
+        section = self.get_section()
+        self.assertEquals('b', section.get('a'))
+        self.assertEquals('y', section.get('x'))
 
     def test_wrong_syntax(self):
         self.assertRaises(errors.BzrCommandError,
-                          self.section._from_cmdline, ['a=b', 'c'])
+                          self.store._from_cmdline, ['a=b', 'c'])
 
 
 class TestStore(tests.TestCaseWithTransport):
 
-    def assertSectionContent(self, expected, section):
+    def assertSectionContent(self, expected, (store, section)):
         """Assert that some options have the proper values in a section."""
         expected_name, expected_options = expected
         self.assertEquals(expected_name, section.id)
@@ -2983,13 +2989,13 @@
 
     def setUp(self):
         super(TestConcurrentStoreUpdates, self).setUp()
-        self._content = 'one=1\ntwo=2\n'
         self.stack = self.get_stack(self)
         if not isinstance(self.stack, config._CompatibleStack):
             raise tests.TestNotApplicable(
                 '%s is not meant to be compatible with the old config design'
                 % (self.stack,))
-        self.stack.store._load_from_string(self._content)
+        self.stack.set('one', '1')
+        self.stack.set('two', '2')
         # Flush the store
         self.stack.store.save()
 
@@ -3170,9 +3176,9 @@
 ''')
         self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
                            '/quux/quux'],
-                          [section.id for section in store.get_sections()])
+                          [section.id for _, section in store.get_sections()])
         matcher = config.LocationMatcher(store, '/foo/bar/quux')
-        sections = list(matcher.get_sections())
+        sections = [section for s, section in matcher.get_sections()]
         self.assertEquals([3, 2],
                           [section.length for section in sections])
         self.assertEquals(['/foo/bar', '/foo'],
@@ -3189,9 +3195,9 @@
 section=/foo/bar
 ''')
         self.assertEquals(['/foo', '/foo/bar'],
-                          [section.id for section in store.get_sections()])
+                          [section.id for _, section in store.get_sections()])
         matcher = config.LocationMatcher(store, '/foo/bar/baz')
-        sections = list(matcher.get_sections())
+        sections = [section for s, section in matcher.get_sections()]
         self.assertEquals([3, 2],
                           [section.length for section in sections])
         self.assertEquals(['/foo/bar', '/foo'],
@@ -3210,7 +3216,7 @@
         matcher = config.LocationMatcher(store, 'dir/subdir')
         sections = list(matcher.get_sections())
         self.assertLength(1, sections)
-        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
+        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
 
     def test_file_urls_are_normalized(self):
         store = self.get_store(self)
@@ -3333,7 +3339,7 @@
         self.assertEquals(None, self.conf.get('foo'))
 
     def test_get_hook(self):
-        self.conf.store._load_from_string('foo=bar')
+        self.conf.set('foo', 'bar')
         calls = []
         def hook(*args):
             calls.append(args)
@@ -3345,12 +3351,17 @@
         self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
 
-class TestStackGetWithConverter(TestStackGet):
+class TestStackGetWithConverter(tests.TestCaseWithTransport):
 
     def setUp(self):
         super(TestStackGetWithConverter, self).setUp()
         self.overrideAttr(config, 'option_registry', config.OptionRegistry())
         self.registry = config.option_registry
+        # We just want a simple stack with a simple store so we can inject
+        # whatever content the tests need without caring about what section
+        # names are valid for a given store/stack.
+        store = config.IniFileStore(self.get_transport(), 'foo.conf')
+        self.conf = config.Stack([store.get_sections], store)
 
     def register_bool_option(self, name, default=None, default_from_env=None):
         b = config.Option(name, help='A boolean.',
@@ -3722,8 +3733,7 @@
 
     def test_simple_set(self):
         conf = self.get_stack(self)
-        conf.store._load_from_string('foo=bar')
-        self.assertEquals('bar', conf.get('foo'))
+        self.assertEquals(None, conf.get('foo'))
         conf.set('foo', 'baz')
         # Did we get it back ?
         self.assertEquals('baz', conf.get('foo'))

=== modified file 'bzrlib/tests/test_debug.py'
--- a/bzrlib/tests/test_debug.py	2011-08-12 16:25:56 +0000
+++ b/bzrlib/tests/test_debug.py	2011-11-16 17:19:13 +0000
@@ -34,7 +34,7 @@
 
     def assertDebugFlags(self, expected_flags, conf_bytes):
         conf = config.GlobalStack()
-        conf.store._load_from_string(conf_bytes)
+        conf.store._load_from_string('[DEFAULT]\n' + conf_bytes)
         conf.store.save()
         self.overrideAttr(debug, 'debug_flags', set())
         debug.set_debug_flags_from_config()

=== modified file 'bzrlib/tests/test_msgeditor.py'
--- a/bzrlib/tests/test_msgeditor.py	2011-06-14 01:26:41 +0000
+++ b/bzrlib/tests/test_msgeditor.py	2011-11-16 17:19:13 +0000
@@ -252,9 +252,9 @@
         self.overrideEnv('VISUAL', 'visual')
         self.overrideEnv('EDITOR', 'editor')
 
-        conf = config.GlobalConfig.from_string('editor = config_editor\n',
-                                               save=True)
-
+        conf = config.GlobalStack()
+        conf.store._load_from_string('[DEFAULT]\neditor = config_editor\n')
+        conf.store.save()
         editors = list(msgeditor._get_editor())
         editors = [editor for (editor, cfg_src) in editors]
 

=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt	2011-11-17 14:46:42 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt	2011-11-17 17:16:09 +0000
@@ -64,6 +64,9 @@
 .. Major internal changes, unlikely to be visible to users or plugin 
    developers, but interesting for bzr developers.
 
+* ``bzr config`` uses the new configuration implementation.
+  (Vincent Ladeuil)
+
 * New HPSS calls ``Repository.has_signature_for_revision_id`` and
   ``Repository.make_working_trees``.  (Jelmer Vernooij)
 




More information about the bazaar-commits mailing list