Rev 5448: Implement config.get_options_matching_regexp. in file:///home/vila/src/bzr/experimental/config/

Vincent Ladeuil v.ladeuil+lp at free.fr
Thu Sep 30 12:10:52 BST 2010


At file:///home/vila/src/bzr/experimental/config/

------------------------------------------------------------
revno: 5448
revision-id: v.ladeuil+lp at free.fr-20100930111052-rue7ftm613c0v1mu
parent: pqm at pqm.ubuntu.com-20100926212028-5i2y4k4yusao7umb
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: config-read
timestamp: Thu 2010-09-30 13:10:52 +0200
message:
  Implement config.get_options_matching_regexp.
  
  * bzrlib/tests/test_config.py:
  (TestConfigDisplay): Tests for get_options_matching_glob and
  get_options_matching_regexp.
  
  * bzrlib/config.py:
  (Config.get_options_matching_glob)
  (IniBasedConfig.get_options_matching_regexp): API to query config
  for a list of config variables and their values in the various
  config involved.
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2010-09-25 20:18:07 +0000
+++ b/NEWS	2010-09-30 11:10:52 +0000
@@ -39,6 +39,11 @@
 API Changes
 ***********
 
+* ``bzrlib.config.IniBasedConfig`` objects now implement
+  ``get_options_matching_glob`` and ``get_options_matching_regexp`` that
+  permit querying them for matching variables, their values and which
+  configuration file define them. (Vincent Ladeuil)
+
 Internals
 *********
 

=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2010-08-29 14:32:45 +0000
+++ b/bzrlib/config.py	2010-09-30 11:10:52 +0000
@@ -69,7 +69,7 @@
 from bzrlib.lazy_import import lazy_import
 lazy_import(globals(), """
 import errno
-from fnmatch import fnmatch
+import fnmatch
 import re
 from cStringIO import StringIO
 
@@ -444,6 +444,57 @@
         """Override this to define the section used by the config."""
         return "DEFAULT"
 
+    def get_options_matching_glob(self, name_glob, sections=None,
+                                  file_name=None):
+        """Return an ordered list of (name, value, section, file_name) tuples.
+
+        This is a convenience on top of ``get_options_matching_regexp``
+        accepting a glob.
+        """
+        name_re = re.compile(fnmatch.translate(name_glob))
+        return self.get_options_matching_regexp(name_re, sections=sections,
+                                                file_name=file_name)
+
+    def get_options_matching_regexp(self, name_re, sections=None,
+                                    file_name=None):
+        """Return an ordered list of (name, value, section, file_name) tuples.
+
+        All variables whose name match ``name_re`` are returned with their
+        associated value and the section they appeared in.
+
+        :param sections: Default to ``_get_matching_sections`` if not
+            specified. This gives a better control to daughter classes about
+            which sections should be searched.
+
+        :param file_name: Which file path should be used in the returned list.
+        """
+        # FIXME: file_name isn't very user friendly, the basename should
+        # probably be displayed (by higher levels) but that won't fly for
+        # branch.conf if several branches are involved (bound branches or heady
+        # checkouts will want to distinguish the master branch). Using the
+        # absolute path should be good enough for a first implementation.
+        # -- vila 20100930
+        matches = []
+        if sections is None:
+            parser = self._get_parser()
+            sections = []
+            for (section_name, _) in self._get_matching_sections():
+                try:
+                    section = parser[section_name]
+                except KeyError:
+                    # This could happen for an empty file for which we define a
+                    # DEFAULT section. FIXME: Force callers to provide sections
+                    # instead ? -- vila 20100930
+                    continue
+                sections.append((section_name, section))
+        if file_name is None:
+            file_name = self.file_name
+        for (section_name, section) in sections:
+            for (name, value) in section.iteritems():
+                if name_re.search(name):
+                    matches.append((name, value, section_name, file_name))
+        return matches
+
     def _get_option_policy(self, section, option_name):
         """Return the policy for the given (section, option_name) pair."""
         return POLICY_NONE
@@ -717,7 +768,7 @@
             names = zip(location_names, section_names)
             matched = True
             for name in names:
-                if not fnmatch(name[0], name[1]):
+                if not fnmatch.fnmatch(name[0], name[1]):
                     matched = False
                     break
             if not matched:
@@ -728,6 +779,7 @@
                 continue
             matches.append((len(section_names), section,
                             '/'.join(location_names[len(section_names):])))
+        # put the longest (aka more specific) locations first
         matches.sort(reverse=True)
         sections = []
         for (length, section, extra_path) in matches:
@@ -900,6 +952,35 @@
                 return value
         return None
 
+    def get_options_matching_glob(self, name_glob, sections=None,
+                                  file_name=None):
+        name_re = re.compile(fnmatch.translate(name_glob))
+        return self.get_options_matching_regexp(name_re, sections=sections,
+                                                file_name=file_name)
+
+    def get_options_matching_regexp(self, name_re, sections=None,
+                                    file_name=None):
+        matches = []
+        location_config = self._get_location_config()
+        matches.extend(location_config.get_options_matching_regexp(name_re))
+        # FIXME: The following is rather convoluted but
+        # BranchConfig/TreeConfig/TransportConfig separation doesn't provide an
+        # easier access. Now that GlobalConfig and Location_Config are
+        # LockableConfig objects that already requires a transport, we may want
+        # to refactor BranchConfig to get a simpler implementation
+        # -- vila 20100930
+        branch_config = self._get_branch_data_config()
+        if sections is None:
+            sections = [('DEFAULT', branch_config._get_parser())]
+        if file_name is None:
+            file_name = branch_config._config._transport.abspath(
+                branch_config._config._filename)
+        matches.extend(branch_config.get_options_matching_regexp(
+                name_re, sections, file_name))
+        global_config = self._get_global_config()
+        matches.extend(global_config.get_options_matching_regexp(name_re))
+        return matches
+
     def set_user_option(self, name, value, store=STORE_BRANCH,
         warn_masked=False):
         if store == STORE_BRANCH:

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2010-08-30 08:26:45 +0000
+++ b/bzrlib/tests/test_config.py	2010-09-30 11:10:52 +0000
@@ -1471,6 +1471,73 @@
         self.assertIs(None, bzrdir_config.get_default_stack_on())
 
 
+class TestConfigDisplay(tests.TestCaseWithTransport):
+
+    def setUp(self):
+        super(TestConfigDisplay, self).setUp()
+        self.global_config = config.GlobalConfig()
+        self.tree = self.make_branch_and_tree('.')
+        self.branch_config = config.BranchConfig(self.tree.branch)
+        self.locations_config = config.LocationConfig(self.tree.basedir)
+
+    def assertMatchingVars(self, expected, conf, name_glob):
+        actual = conf.get_options_matching_glob(name_glob)
+        # Filter the config file name as we don't care here
+        filtered = [(name, value, section)
+                    for name, value, section, file_name in actual]
+        self.assertEqual(expected, filtered)
+
+    # One variable in non of the above
+    def test_no_variable(self):
+        # Using branch should query branch, locations and bazaar
+        self.assertMatchingVars([], self.branch_config, 'file')
+
+    def test_no_matches(self):
+        self.global_config.set_user_option('file', 'bazaar')
+        self.locations_config.set_user_option('file', 'locations')
+        self.branch_config.set_user_option('file', 'branch')
+        self.assertMatchingVars([], self.branch_config, 'not_file')
+
+    def test_variable_in_bazaar(self):
+        self.global_config.set_user_option('file', 'bazaar')
+        self.assertMatchingVars([('file', 'bazaar', 'DEFAULT')],
+                                 self.global_config, 'file')
+
+    def test_variable_in_locations(self):
+        self.locations_config.set_user_option('file', 'locations')
+        self.assertMatchingVars([('file', 'locations', self.tree.basedir)],
+                                 self.locations_config, 'file')
+
+    def test_variable_in_branch(self):
+        self.branch_config.set_user_option('file', 'branch')
+        self.assertMatchingVars([('file', 'branch', 'DEFAULT')],
+                                 self.branch_config, 'file')
+
+    def test_variable_in_bazaar_and_branch(self):
+        self.global_config.set_user_option('file', 'bazaar')
+        self.branch_config.set_user_option('file', 'branch')
+        self.assertMatchingVars([('file', 'branch', 'DEFAULT'),
+                                 ('file', 'bazaar', 'DEFAULT'),],
+                                self.branch_config, 'file')
+
+    def test_variable_in_branch_and_locations(self):
+        # Hmm, locations override branch :-/
+        self.locations_config.set_user_option('file', 'locations')
+        self.branch_config.set_user_option('file', 'branch')
+        self.assertMatchingVars([('file', 'locations', self.tree.basedir),
+                                 ('file', 'branch', 'DEFAULT'),],
+                                self.branch_config, 'file')
+
+    def test_variable_in_bazaar_locations_and_branch(self):
+        self.global_config.set_user_option('file', 'bazaar')
+        self.locations_config.set_user_option('file', 'locations')
+        self.branch_config.set_user_option('file', 'branch')
+        self.assertMatchingVars([('file', 'locations', self.tree.basedir),
+                                 ('file', 'branch', 'DEFAULT'),
+                                 ('file', 'bazaar', 'DEFAULT'),],
+                                self.branch_config, 'file')
+
+
 class TestAuthenticationConfigFile(tests.TestCase):
     """Test the authentication.conf file matching"""
 



More information about the bazaar-commits mailing list