Rev 6252: (vila) Implement 'relpath' as a section local option. (Vincent Ladeuil) in file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/
Patch Queue Manager
pqm at pqm.ubuntu.com
Thu Nov 10 09:48:32 UTC 2011
At file:///srv/pqm.bazaar-vcs.org/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 6252 [merge]
revision-id: pqm at pqm.ubuntu.com-20111110094831-vrdth18b7o44qi70
parent: pqm at pqm.ubuntu.com-20111109160934-uzl2bf236oib7u3x
parent: v.ladeuil+lp at free.fr-20111105161211-b4b82psc3mw23i37
committer: Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2011-11-10 09:48:31 +0000
message:
(vila) Implement 'relpath' as a section local option. (Vincent Ladeuil)
modified:
bzrlib/config.py config.py-20051011043216-070c74f4e9e338e8
bzrlib/help_topics/en/configuration.txt configuration.txt-20060314161707-868350809502af01
bzrlib/tests/test_config.py testconfig.py-20051011041908-742d0c15d8d8c8eb
doc/developers/configuration.txt configuration.txt-20110408142435-korjxxnskvq44sta-1
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-11 12:01:51 +0000
+++ b/bzrlib/config.py 2011-10-31 22:11:59 +0000
@@ -3008,6 +3008,7 @@
super(LocationSection, self).__init__(section.id, section.options)
self.length = length
self.extra_path = extra_path
+ self.locals = {'relpath': extra_path}
def get(self, name, default=None):
value = super(LocationSection, self).get(name, default)
@@ -3016,6 +3017,19 @@
policy = _policy_value.get(policy_name, POLICY_NONE)
if policy == POLICY_APPENDPATH:
value = urlutils.join(value, self.extra_path)
+ # expand section local options right now (since POLICY_APPENDPATH
+ # will never add options references, it's ok to expand after it).
+ chunks = []
+ for is_ref, chunk in iter_option_refs(value):
+ if not is_ref:
+ chunks.append(chunk)
+ else:
+ ref = chunk[1:-1]
+ if ref in self.locals:
+ chunks.append(self.locals[ref])
+ else:
+ chunks.append(chunk)
+ value = ''.join(chunks)
return value
@@ -3081,18 +3095,26 @@
yield section
+_option_ref_re = lazy_regex.lazy_compile('({[^{}]+})')
+"""Describes an expandable option reference.
+
+We want to match the most embedded reference first.
+
+I.e. for '{{foo}}' we will get '{foo}',
+for '{bar{baz}}' we will get '{baz}'
+"""
+
+def iter_option_refs(string):
+ # Split isolate refs so every other chunk is a ref
+ is_ref = False
+ for chunk in _option_ref_re.split(string):
+ yield is_ref, chunk
+ is_ref = not is_ref
+
+
class Stack(object):
"""A stack of configurations where an option can be defined"""
- _option_ref_re = lazy_regex.lazy_compile('({[^{}]+})')
- """Describes an exandable option reference.
-
- We want to match the most embedded reference first.
-
- I.e. for '{{foo}}' we will get '{foo}',
- for '{bar{baz}}' we will get '{baz}'
- """
-
def __init__(self, sections_def, store=None, mutable_section_name=None):
"""Creates a stack of sections with an optional store for changes.
@@ -3210,19 +3232,15 @@
result = string
# We need to iterate until no more refs appear ({{foo}} will need two
# iterations for example).
- while True:
- raw_chunks = Stack._option_ref_re.split(result)
- if len(raw_chunks) == 1:
- # Shorcut the trivial case: no refs
- return result
+ expanded = True
+ while expanded:
+ expanded = False
chunks = []
- # Split will isolate refs so that every other chunk is a ref
- chunk_is_ref = False
- for chunk in raw_chunks:
- if not chunk_is_ref:
+ for is_ref, chunk in iter_option_refs(result):
+ if not is_ref:
chunks.append(chunk)
- chunk_is_ref = True
else:
+ expanded = True
name = chunk[1:-1]
if name in _refs:
raise errors.OptionExpansionLoop(string, _refs)
@@ -3232,7 +3250,6 @@
raise errors.ExpandingUnknownOption(name, string)
chunks.append(value)
_refs.pop()
- chunk_is_ref = False
result = ''.join(chunks)
return result
@@ -3242,11 +3259,6 @@
# anything else
value = env[name]
else:
- # FIXME: This is a limited implementation, what we really need is a
- # way to query the bzr config for the value of an option,
- # respecting the scope rules (That is, once we implement fallback
- # configs, getting the option value should restart from the top
- # config, not the current one) -- vila 20101222
value = self.get(name, expand=False)
value = self._expand_options_in_string(value, env, _refs)
return value
=== modified file 'bzrlib/help_topics/en/configuration.txt'
--- a/bzrlib/help_topics/en/configuration.txt 2011-08-19 22:26:03 +0000
+++ b/bzrlib/help_topics/en/configuration.txt 2011-11-05 16:12:11 +0000
@@ -221,7 +221,7 @@
~~~~~~~~~~~~~~
An ini file has three types of contructs: section headers, section
-variables and comments.
+options and comments.
Comments
^^^^^^^^
@@ -240,9 +240,9 @@
The only valid section headers for bazaar.conf currently are [DEFAULT] and
[ALIASES]. Section headers are case sensitive. The default section provides for
-setting variables which can be overridden with the branch config file.
+setting options which can be overridden with the branch config file.
-For ``locations.conf``, the variables from the section with the
+For ``locations.conf``, the options from the section with the
longest matching section header are used to the exclusion of other
potentially valid section headers. A section header uses the path for
the branch as the section header. Some examples include::
@@ -251,29 +251,27 @@
[/home/jdoe/branches/]
-Section variables
-^^^^^^^^^^^^^^^^^
+Section options
+^^^^^^^^^^^^^^^
-A section variable resides within a section. A section variable contains a
-variable name, an equals sign and a value. For example::
+A section option resides within a section. A section option contains an
+option name, an equals sign and a value. For example::
email = John Doe <jdoe at isp.com>
gpg_signing_key = Amy Pond <amy at example.com>
-A variable can reference other variables **in the same configuration file** by
-enclosing them in curly brackets::
+A option can reference other options by enclosing them in curly brackets::
my_branch_name = feature_x
my_server = bzr+ssh://example.com
push_location = {my_server}/project/{my_branch_name}
-
-Variable policies
-^^^^^^^^^^^^^^^^^
-
-Variables defined in a section affect the named directory or URL plus
-any locations they contain. Policies can be used to change how a
-variable value is interpreted for contained locations. Currently
+Option policies
+^^^^^^^^^^^^^^^
+
+Options defined in a section affect the named directory or URL plus
+any locations they contain. Policies can be used to change how an
+option value is interpreted for contained locations. Currently
there are three policies available:
none:
@@ -286,7 +284,7 @@
for contained locations, any additional path components are
appended to the value.
-Policies are specified by keys with names of the form "$var:policy".
+Policies are specified by keys with names of the form "<option_name>:policy".
For example, to define the push location for a tree of branches, the
following could be used::
@@ -297,6 +295,33 @@
With this configuration, the push location for ``/top/location/branch1``
would be ``sftp://example.com/location/branch1``.
+Section local options
+^^^^^^^^^^^^^^^^^^^^^
+
+Some options are defined automatically inside a given section and can be
+refered to in this section only.
+
+For example, the ``appendpath`` policy can be used like this::
+
+ [/home/vila/src/bzr/bugs]
+ mypush = lp:~vila/bzr
+ mypush:policy=appendpath
+
+Using ``relpath`` to achieve the same result is done like this::
+
+ [/home/vila/src/bzr/bugs]
+ mypush = lp:~vila/bzr/{relpath}
+
+In both cases, when used in a directory like
+``/home/vila/src/bzr/bugs/832013-expand-in-stack`` we'll get::
+
+ $ bzr config mypush
+ lp:~vila/bzr/832013-expand-in-stack
+
+When an option is local to a Section, it cannot be referred to from option
+values in any other section from the same ``Store`` nor from any other
+``Store``.
+
The main configuration file, bazaar.conf
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -337,14 +362,14 @@
of bzr that requires authentication (smtp for example).
The syntax of the file obeys the same rules as the others except for the
-variable policies which don't apply.
+option policies which don't apply.
For more information on the possible uses of the authentication configuration
file see :doc:`authentication-help`.
-Common variable options
------------------------
+Common options
+--------------
debug_flags
~~~~~~~~~~~
=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py 2011-10-06 07:08:29 +0000
+++ b/bzrlib/tests/test_config.py 2011-10-31 22:11:59 +0000
@@ -3453,6 +3453,35 @@
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
+class TestIterOptionRefs(tests.TestCase):
+ """iter_option_refs is a bit unusual, document some cases."""
+
+ def assertRefs(self, expected, string):
+ self.assertEquals(expected, list(config.iter_option_refs(string)))
+
+ def test_empty(self):
+ self.assertRefs([(False, '')], '')
+
+ def test_no_refs(self):
+ self.assertRefs([(False, 'foo bar')], 'foo bar')
+
+ def test_single_ref(self):
+ self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
+
+ def test_broken_ref(self):
+ self.assertRefs([(False, '{foo')], '{foo')
+
+ def test_embedded_ref(self):
+ self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
+ '{{foo}}')
+
+ def test_two_refs(self):
+ self.assertRefs([(False, ''), (True, '{foo}'),
+ (False, ''), (True, '{bar}'),
+ (False, ''),],
+ '{foo}{bar}')
+
+
class TestStackExpandOptions(tests.TestCaseWithTransport):
def setUp(self):
@@ -3607,6 +3636,88 @@
self.assertEquals('quux', c.get('bar', expand=True))
+class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
+
+ def test_cross_global_locations(self):
+ l_store = config.LocationStore()
+ l_store._load_from_string('''
+[/branch]
+lfoo = loc-foo
+lbar = {gbar}
+''')
+ l_store.save()
+ g_store = config.GlobalStore()
+ g_store._load_from_string('''
+[DEFAULT]
+gfoo = {lfoo}
+gbar = glob-bar
+''')
+ g_store.save()
+ stack = config.LocationStack('/branch')
+ self.assertEquals('glob-bar', stack.get('lbar', expand=True))
+ self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
+
+
+class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
+
+ def test_expand_relpath_locally(self):
+ l_store = config.LocationStore()
+ l_store._load_from_string('''
+[/home/user/project]
+lfoo = loc-foo/{relpath}
+''')
+ l_store.save()
+ stack = config.LocationStack('/home/user/project/branch')
+ self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
+
+ def test_expand_relpath_unknonw_in_global(self):
+ g_store = config.GlobalStore()
+ g_store._load_from_string('''
+[DEFAULT]
+gfoo = {relpath}
+''')
+ g_store.save()
+ stack = config.LocationStack('/home/user/project/branch')
+ self.assertRaises(errors.ExpandingUnknownOption,
+ stack.get, 'gfoo', expand=True)
+
+ def test_expand_local_option_locally(self):
+ l_store = config.LocationStore()
+ l_store._load_from_string('''
+[/home/user/project]
+lfoo = loc-foo/{relpath}
+lbar = {gbar}
+''')
+ l_store.save()
+ g_store = config.GlobalStore()
+ g_store._load_from_string('''
+[DEFAULT]
+gfoo = {lfoo}
+gbar = glob-bar
+''')
+ g_store.save()
+ stack = config.LocationStack('/home/user/project/branch')
+ self.assertEquals('glob-bar', stack.get('lbar', expand=True))
+ self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
+
+ def test_locals_dont_leak(self):
+ """Make sure we chose the right local in presence of several sections.
+ """
+ l_store = config.LocationStore()
+ l_store._load_from_string('''
+[/home/user]
+lfoo = loc-foo/{relpath}
+[/home/user/project]
+lfoo = loc-foo/{relpath}
+''')
+ l_store.save()
+ stack = config.LocationStack('/home/user/project/branch')
+ self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
+ stack = config.LocationStack('/home/user/bar/baz')
+ self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
+
+
+
class TestStackSet(TestStackWithTransport):
def test_simple_set(self):
=== modified file 'doc/developers/configuration.txt'
--- a/doc/developers/configuration.txt 2011-09-07 15:21:39 +0000
+++ b/doc/developers/configuration.txt 2011-11-05 16:12:11 +0000
@@ -47,6 +47,7 @@
MutableSection is needed to set or remove an option, ReadOnlySection should
be used otherwise.
+
Stores
------
@@ -79,7 +80,7 @@
------------------
For some contexts, only some sections from a given store will apply. Defining
-which is what the ``SectionMatcher`` are about.
+which is what the ``SectionMatcher`` objects are about.
The main constraint here is that a ``SectionMatcher`` should delay the loading
of the associated store as long as possible. The constructor should collect
@@ -92,7 +93,15 @@
to implement the ``appendpath`` policy for example). If no sections match,
an empty list is returned.
-.. FIXME: Replace the appendpath example if/when it's deprecated ;)
+Options local to a section can also be defined for special purposes and be
+handled by ``Section.get()``. One such option is ``relpath`` which is
+defined in ``LocationSection`` as an alternative to the ``appendpath``
+policy.
+
+For ``appendpath``, the ``LocationSection`` will carry ``extra_path`` as
+``832013-expand-in-stack`` and ``relpath`` will be available as a
+``Section`` local option with the same value. Note that such options can
+only be expanded inside the section that defines them.
Specific section matchers can be implemented by overriding ``get_sections``
or just ``match``.
=== modified file 'doc/en/release-notes/bzr-2.5.txt'
--- a/doc/en/release-notes/bzr-2.5.txt 2011-11-08 19:09:55 +0000
+++ b/doc/en/release-notes/bzr-2.5.txt 2011-11-10 09:48:31 +0000
@@ -25,6 +25,12 @@
revisions. I.e.: ``bzr pull -v -Olog_format=short`` will use the ``short``
format instead of the default ``long`` one. (Vincent Ladeuil, #861472)
+* The new config scheme allows an alternative syntax for the 'appenpath'
+ policy relying on option expansion and defining a new 'relpath' option
+ local to a section. Instead of using '<option>:policy=appendpath', the
+ option value can de defined as 'option=xxxx/{relpath}'.
+ (Vincent Ladeuil, #832013)
+
Improvements
************
More information about the bazaar-commits
mailing list