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